Wrapping Auth0's parseHash function in a Promise - javascript

auth0.js has a function that's used to parse the URL hash fragment and extract the authentication result therefrom. I'm wrapping this function within one called loadSession as follows:
public loadSession(): void {
this.auth0.parseHash((err, authResult) => {
if (authResult) {
window.location.hash = '';
localStorage.setItem('token', authResult.accessToken);
// TODO (1)
} else if (err) {
// TODO (2)
}
});
}
As seen above, parseHash takes a callback function as an argument and I cannot control that. I would like loadSession to return a Promise that would be resolved at // TODO (1) and rejected at // TODO (2) above. This way I can do obj.loadSession().then(() => { // do something if successful }).catch((err) => { // raise error if not })

Simply wrap it inside a promise:
public loadSession() {
return new Promise((resolve, reject) => {
this.auth0.parseHash((err, authResult) => {
if(authResult) {
window.location.hash = '';
localStorage.setItem('token', authResult.accessToken);
resolve(authResult);
} else if (err) {
reject(err);
}
});
});
}

You can pretty much pass any callback function to a function that returns a promise given:
The callback is the last argument
The callback function takes error as it's first argument
Here is an example:
const asPromise =
(context) =>
(fn) =>
(args) =>
new Promise(
(resolve,reject) =>
fn.apply(
context,
(args||[]).concat(
function(){
if(arguments[0]){
reject(arguments[0]);return;
}
resolve(Array.from(arguments).slice(1));
}
)
)
);
// to apply parseHash on auth0
public loadSession(): Promise {
return asPromise(this.auth0)(this.auth0.parseHash)()
.then(
([authResult])=>{
if (authResult) {
window.location.hash = '';
localStorage.setItem('token', authResult.accessToken);
//whatever you return here is the resolve
return authResult;
}
//just throw in a promise handler will return a rejected promise
// this is only for native promises, some libraries don't behave
// according to spec so you should test your promise polyfil if supporting IE
throw "Promise resolved but got no authResult";
}
)
}

public loadSession(): Promise {
return new Promise((resolve, reject) => {
this.auth0.parseHash((err, authResult) => {
if (authResult) {
window.location.hash = '';
localStorage.setItem('token', authResult.accessToken);
// TODO (1)
// resolve(something)
} else if (err) {
// TODO (2)
// reject(something)
}
});
}
For more information about using Promise API, you can visit MDN Docs

Or you can use a tiny library that does that for you: promisify-auth0 on GitHub, promisify-auth0 on npmjs.org.
Now updated to version 9.5.1.

Related

Turning a callback function into promise

I'm trying to convert this code into promise (The one that i commented //). My goal is to print the user's location (longitude and latitude) but I'm having a hard time figuring it out, on how to convert this into Promise. (Sorry for my english)
// const getUserLocation = () => {
// if (navigator.geolocation) {
// navigator.geolocation.getCurrentPosition(succes, error);
// } else {
// console.log('Your browser does not support geolocation');
// }
// }
// const succes = (positon) => {
// console.log(positon.coords.latitude)
// console.log(positon.coords.longitude)
// }
// const error = (err) => {
// console.log('The User have denied the request for Geolocation.');
// }
// getUserLocation();
const getUserLocation = () => {
return new Promise((resolve, reject) => {
if (navigator.geolocation) {
resolve(navigator.geolocation.getCurrentPosition);
} else {
reject('The User have denied the request for Geolocation.');
}
})
}
getUserLocation()
.then((response) => {
console.log(response.coords.longitude);
console.log(response.coords.latitude);
})
.catch((err) => {
console.log(err);
})
You are very close, the promise variant should look like this:
const getUserLocation = () => {
return new Promise((resolve, reject) => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(resolve, reject);
} else {
reject('The User have denied the request for Geolocation.');
}
})
}
In the code above you pass the resolve and reject functions to getCurrentPosition() which calls them with either the geolocation position, or an error (in the case of reject) as sole argument.
I'd just pass resolve and reject from the Promise function to the getCurrentPosition function.
const getUserLocation = () => {
return new Promise((resolve, reject) => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(resolve, reject)
} else {
reject('The User have denied the request for Geolocation.');
}
})
}
getUserLocation()
.then((response) => {
console.log('success:', response.coords.longitude, response.coords.latitude);
})
.catch((err) => {
console.log('error:', err);
})
No. That's not the way to do it. There is nothing special about promises. It is not a new syntax added to the language. It is just an ordinary constructor/class that you can write yourself in pure javascript. Therefore there is nothing special that resolve() can do to convert callbacks to promises.
The correct way is to pass the value passed to the callback to the resolve() function:
// To avoid confusion I rename the resolve() and reject() function to
// AAA and BBB. This is to illustrate that they are just variables
// **you** declare and do not have any special syntax. This is also
// to avoid confusion with the Promise.resolve() static method.
const getUserLocation = () => {
return new Promise((AAA, BBB) => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(succes, error);
} else {
console.log('Your browser does not support geolocation');
BBB(new Error('Your browser does not support geolocation')); // optional
}
const succes = (positon) => {
console.log(positon.coords.latitude)
console.log(positon.coords.longitude)
AAA(position); // This is how you correctly resolve()
}
const error = (err) => {
console.log('The User have denied the request for Geolocation.');
BBB(err); // This is how you correctly reject()
}
});
}
Basically you need to copy your success() and error() functions into the scope of your getUserLocation() function and then you MUST resolve or reject inside those functions.

named promise chainy for normal promises that handle events

I am trying to create a named promise chain. I am not sure of how to achieve this. The goal is following:
function multiplepromises() {
var prom = function (resolve, reject) {
var lifecycleeventone = new someEvent();
var lifecycleeventtwo = new someEvent();
var lifecycleeventthree = new someEvent();
var lifecycleeventfour = new someEvent();
var lifecycleeventfive = new someEvent();
lifecycleeventone.on(function () {
try {
resolve("eventone")
} catch {
reject("eventone")
}
})
lifecycleeventtwo.on(function () {
try {
resolve("eventtwo")
} catch {
reject("eventtwo")
}
})
lifecycleeventthree.on(function () {
try {
resolve("eventthree")
} catch {
reject("eventthree")
}
})
lifecycleeventfour.on(function () {
try {
resolve("eventfour")
} catch {
reject("eventfour")
}
})
lifecycleeventfive.on(function () {
try {
resolve("eventfive")
} catch {
reject("eventfive")
}
})
maineventlikefinallySOcalledalways.on(function(){
try {
resolve("maineventlikefinallySOcalledalways")
} catch {
reject("maineventlikefinallySOcalledalways")
}
})
}
return prom
}
multiplepromises()
.onlifecycleeventoneAsProm((result)=> result) //eventone promise resolve
.onlifecycleeventoneerrorAsProm((error)=> error) //eventone
.onlifecycleeventtwoAsProm((result)=> result) //eventtwo promise resolve
.onlifecycleeventtwoerrorAsProm((error)=> error) //eventtwo
.onlifecycleeventthreeAsProm((result)=> result) //eventthree promise resolve
.onlifecycleeventthreeerrorAsProm((error)=> error) //eventthree
.onlifecycleeventfourAsProm((result)=> result) //eventfour promise resolve
.onlifecycleeventfourerrorAsProm((error)=> error) //eventfour
.onlifecycleeventfiveAsProm((result)=> result) // eventfive promise resolve
.onlifecycleeventfiveerrorAsProm((error)=> error) //eventfive
.then((result)=> result) // maineventlikefinallySOcalledalways promise resolve
.error((error)=> error) // maineventlikefinallySOcalledalways promise reject
multiplepromises()
.onlifecycleeventoneAsProm((result)=> result) //eventone promise resolve
.onlifecycleeventoneerrorAsProm((error)=> error) //eventone
.onlifecycleeventtwoAsProm((result)=> result) //eventtwo promise resolve
.onlifecycleeventtwoerrorAsProm((error)=> error) //eventtwo
.onlifecycleeventthreeAsProm((result)=> console.log("test"))
// lifecycleeventthree promise reject stops here and
// doesnt continue to .then/.error since there was no return from this lifecycle event(promise)
I have read this and this doesnt solve the purpose completely.
Handling multiple catches in promise chain and https://javascript.info/promise-chaining
Dont want to use Rx and want to keep to vanilla js
You can‘t achieve something like that with Promises.
Instead you can make a function that returns an object with event registrar functions, which return again the object.
Here is a simple example:
function test() {
return new (function() {
this.eh = {};
this.on = (event, handler) => {
this.eh[event] = handler;
return this;
}
this.call = (event, ...args) => {
if (typeof this.eh[event] === 'function') {
this.eh[event](...args);
}
}
Promise.resolve().then(() => {
// Do your stuff...
// Example:
this.call('msg', 'This is a message.');
setTimeout(() => {
this.call('some-event', 'This is some event data.');
this.call('error', 'This is an error.');
}, 1000);
});
})()
}
test()
.on('msg', (msg) => console.log(`Message: ${msg}`))
.on('some-event', (data) => console.log(`Some event: ${data}`))
.on('error', (err) => console.log(`Error: ${err}`))
I hope that‘s what you were up to.
Edit:
Here's another attempt: https://jsfiddle.net/bg7oyxau/

Promise chaining with functions

I am trying to use promises for a small new project. But I have some problems with understanding how I could organize my code better, with functions. Problem is, I want my functions to handle things, and my main code to handle other things. So let's see this code:
function doSomething (isGoingToResolve = true) {
return new Promise((resolve, reject) => {
if (isGoingToResolve) {
resolve("something")
} else {
reject("something else")
}
}).then(response => {
console.log("in my function",response)
}).catch(error => {
console.log("in my function",error)
})
}
doSomething().then(response => {
console.log("in my main call", response)
})
With this code, the console will log in my function something and in my main call undefined: https://jsfiddle.net/hkacvw2g/
So I found two solutions to solve my problem, but I just don't like them:
The first one is to create a variable, use a .then() on the variable, and then return the variable (https://jsfiddle.net/hkacvw2g/1/):
function doSomething (isGoingToResolve = true) {
let promise = new Promise((resolve, reject) => {
if (isGoingToResolve) {
resolve("something")
} else {
reject("something else")
}
})
promise.then(response => {
console.log("in my function",response)
}).catch(error => {
console.log("in my function",error)
})
return promise
}
And the second solution (https://jsfiddle.net/hkacvw2g/2/):
function doSomething (isGoingToResolve = true) {
return new Promise((resolve, reject) => {
if (isGoingToResolve) {
resolve("something")
} else {
reject("something else")
}
}).then(response => {
console.log("in my function",response)
return new Promise(resolve => {
resolve(response)
})
}).catch(error => {
console.log("in my function",error)
return new Promise((resolve,reject) => {
reject(error)
})
})
}
Am I missing a better solution to solve my problem?
You can simply return the value (or re-throw the error) and it will be resolved in promise chain:
function doSomething (isGoingToResolve = true) {
return new Promise((resolve, reject) => {
if (isGoingToResolve) {
resolve("something")
} else {
reject("something else")
}
}).then(response => {
console.log("in my function",response)
return response;
}).catch(error => {
console.log("in my function",error)
throw error;
})
}
You might not want that throw error, it depends on how you want to handle your rejections. This way when you re-throw the error, you should catch it when calling the doSomething() method.
You can write a tap function, to tap into a promise chain, and do something while passing along the result, and a parallel tapCatch function, to tap into errors while rethrowing them:
const tap = fn => value => { fn(value); return value; };
const tapCatch = fn => reason => { fn(reason); throw reason; };
Now you can write your code as:
function doSomething(isGoingToResolve = true) {
(isGoingToResolve ?
Promise.resolve("something") :
Promise.reject("something else")
)
.then( tap (response => console.log("in my function", response)))
.catch(tapCatch(error => console.log("in my function", error)));
}
doSomething()
.then(response => console.log("in my main call", response));
However, actually your first solution is better. It reduces the risk of messing up the promise chain by inadvertently forgetting to, or not realizing that you have to, return the original value in then clauses, or rethrow in catch clauses which such clauses are meant only for logging purposes or other side-effects.
You could also pollute the Promise prototype with something like tap (we'll call it thenDo), making it a bit easier to use:
Promise.prototype.thenDo = function(ifFulfilled, ifRejected) {
return this.then(
value => { ifFulfilled(value); return value; },
reason => { ifRejected(reason); throw reason; });
};
Promise.prototype.catchDo = function(ifRejected) {
return this.catch(reason => { ifRejected(reason); throw reason; });
};
Now you can write
function doSomething(isGoingToResolve = true) {
(isGoingToResolve ?
Promise.resolve("something") :
Promise.reject("something else")
)
.thenDo (response => console.log("in my function", response))
.catchDo(error => console.log("in my function", error));
}

I can't make my Promise to properly reject with a new Error message, caught from a try/catch

I have a Promise method that parses links from the web. It returns an Object which I try to access a link key from, but when this Object is empty, it somehow skips the if I have to check its length, causing a scandalous error. Below the codes.
First, the method that is a Promise to parse the links:
* parseReporter() {
const article = new Promise((resolve, reject) => {
parser.parseURL(`https://www.google.com/alerts/feeds/${this.googleAlertsUrlId}/${this.reporterUrlId}`, (err, parsed) => {
if (err) {
reject(new Error(err))
}
if (parsed.feed.entries.length === 0) {
reject(new Error('Nothing to parse'))
}
const link = parsed.feed.entries[0].link
const betterLink = link.substring(42, link.indexOf('&ct='))
mercury.parse(betterLink).then((data) => {
resolve(data)
}).catch((err) => {
reject(new Error(err))
})
})
})
return article
}
And then, here's the method that calls this parseReporter():
* _getLastestNews(userReporter) {
const reportersOperation = new ReportersOperation()
reportersOperation.googleAlertsUrlId = userReporter.url.split('/')[3]
reportersOperation.reporterUrlId = userReporter.url.split('/')[4]
try {
return yield reportersOperation.parseReporter()
} catch (e) {
this.addError(HTTPResponse.STATUS_INTERNAL_SERVER_ERROR, e.message)
return false
}
}
The error is caused when it tries to access link from parsed.feed.entries[0]. I've already logged out the length, and I confirmed to do work and show a number, but it insists on skipping it. Am I doing something wrong with the Promise it try/catch themselves?
reject doesn't "stop" or "return" from a function like return
Therefore, your code is checking for error conditions, but continuing on, as if the data is OK
By adding return before the call to reject, you'll stop this from happening
Just the area of code with changes shown:
// snip
if (err) {
return reject(new Error(err))
}
if (parsed.feed.entries.length === 0) {
return reject(new Error('Nothing to parse'))
}
const link = parsed.feed.entries[0].link
const betterLink = link.substring(42, link.indexOf('&ct='))
//snip
Besides, what Jaramonda suggested, you're also using an anti-pattern when you have a promise and you do resolve and reject in both paths. You can do that much more efficiently:
resolve(mercury.parse(betterLink));
But, what you really should do is you should promisify parser.parseURL() so you can write all the control flow logic using promises. This is much more foolproof and creates a reusable interface that uses promises that you can use elsewhere:
// make promisified version of parser.parseURL()
parser.parseURLP = function (url) {
return new Promise((resolve, reject) => {
parser.parseURL(url, (err, parsed) => {
if (err) return reject(new Error(err));
resolve(parsed);
});
});
};
function parseReporter() {
return parser.parseURL(`https://www.google.com/alerts/feeds/${this.googleAlertsUrlId}/${this.reporterUrlId}`).then(parsed => {
if (parsed.feed.entries.length === 0) {
throw new Error('Nothing to parse');
}
const link = parsed.feed.entries[0].link
const betterLink = link.substring(42, link.indexOf('&ct='))
return mercury.parse(betterLink).catch(err => {
// wrap error in Error object
throw new Error(err);
})
})
}

JavaScript - Return promise AND/OR call callback?

I often see in other peoples docs something like:
Callback is optional, if omitted returns a promise.
This is what I have:
export function doSomeAsync(options, callback) {
const useCallback = (callback && typeof callback == 'function');
const promise = new Promise((resolve, reject) => {
// --- do async stuff here ---
const check = (options.num === 1) ? true : false;
setTimeout(() => {
if (check) {
finish(true, "Number is 1");
} else {
finish(false, new Error("Number is not 1"));
}
}, 1000);
// ---------------------------
function finish(ok, rtn) {
if (useCallback) {
if (ok) {
callback(null, rtn);
} else {
callback(rtn, null);
}
} else {
if (ok) {
resolve(rtn);
} else {
reject(rtn);
}
}
}
});
return (useCallback) ? false : promise;
}
The finish() function just avoids lots of if... statements scattered around.
I'm creating a promise object, whether or not I use it.
Testing like this:
doSomeAsync({ num: 1 }).then((result) => {
console.log('p result', result);
}).catch((err) => {
console.log('p err', err);
});
doSomeAsync({ num: 1 }, (err, result) => {
if (err) {
console.log('cb err', err);
} else {
console.log('cb result', result);
}
});
This works, but I'm wondering if this is the best way, or if others have a better and more succinct implementation..?
This could be simplified if you simply always used the promise, which you're always creating anyway:
export function doSomeAsync(options, callback) {
const promise = new Promise((resolve, reject) => {
const check = (options.num === 1) ? true : false;
setTimeout(() => {
if (check) {
resolve("Number is 1");
} else {
reject(new Error("Number is not 1"));
}
}, 1000);
});
if (callback && typeof callback == 'function') {
promise.then(callback.bind(null, null), callback);
}
return promise;
}
Your function is always promise-based, also in the fact that it always returns a promise. The caller is simply free to ignore that. The callback argument is merely a "legacy fallback interface" (or "alternative interface" if you prefer) to using that promise.
You could get rid of all edge cases by always returning a promise, and define a default callback (a callback-shaped identity function) that handles the no-callback-supplied case:
const genericAsync = (stuff, callback = (e, i) => e || i) => new Promise(
(resolve, reject) => doStuffWith(stuff, resolve, reject)
)
.then(response => callback(null, response))
.catch(callback);

Categories