How To Turn Nested Callback Into Promise?
Solution 1:
I imagine using bluebirds promisify, you would promisify bcrypt.compare like so (you don't HAVE to use the Async part of the name)
let compareAsync = Promise.promisify(bcrypt.compare);
because
userObject
in the first .then needs to be used in the second .then, you can't simply chain the .then's by returning compareAsync, because then the next .then wont have access touserObject
one fix, is to use a variable that will be in scope for both .then's (but ugh)
username = username.toUpperCase();
let text = "SELECT * FROM users WHERE username = $1";
let values = [username];
let uo; //hacky the outer scoped variable
database.one(text, values).then(function (userObject) {
uo = userObject;
returncompareAsync(password, userObject.password);
}).then(function(same) {
if (!same) {
thrownewError("Password mismatched!");
}
const serializeObject = {_id: uo._id};
returnnext(null, serializeObject);
}).catch(function (err) {
returnnext(err, null);
});
another (in my opinion cleaner) option is a nested .then
username = username.toUpperCase();
let text = "SELECT * FROM users WHERE username = $1";
let values = [username];
database.one(text, values).then(function (userObject) {
returncompareAsync(password, userObject.password)
// [optional] following three lines to generate a "nicer" error for compare failure
.catch(function(err) {
throw"bcrypt.compare failed";
})
// nested .then to pass on the userObject and same at the same time
.then(function (same) {
return { same: same, userObject: userObject };
});
}).then(function (result) {
let same = result.same,
userObject = result.userObject;
if (!same) {
thrownewError("Password mismatched!");
}
let serializeObject = { _id: userObject._id };
returnnext(null, serializeObject);
}).catch(function (err) {
returnnext(err, null);
});
NOTE: bluebird has a promisifyAll function ... that promisifies functions in an object, and adds (by default) the Async
postfix to the function name - I believe you can decide on a different postfix name, but the documentation will tell you more
when promisifying a single function, you declare the name yourself - the above could've easily been
let trumpIsBigly = Promise.promisify(bcrypt.compare);
then you would just use trumpIsBigly
where the code has compareAsync
One last possibility
A hand rolled promisified compareAsync (lifted mostly from vitaly-t's answer but with additions)
functioncompareAsync(password1, password2, inValue) {
returnnewPromise(function (resolve, reject) {
bcrypt.compare(password1, password2, function (err, same) {
err = err || (!same && newError("Password mismatched!"));
if (err) {
reject(err);
} else {
resolve(inValue);
}
});
});
}
Now compareAsync will resolve to the incoming value inValue
only if there's no error, AND same is true
username = username.toUpperCase();
let text = "SELECT * FROM users WHERE username = $1";
let values = [username];
database.one(text, values).then(function (userObject) {
returncompareAsync(password, userObject.password, userObject)
}).then(function (userObject) {
let serializeObject = { _id: userObject._id };
returnnext(null, serializeObject);
}).catch(function (err) {
returnnext(err, null);
});
Which makes the "chain" very simple!
Solution 2:
This is to extend on @Jaromanda's answer, in case you use only that one function, and just want to see how to promisify it manually.
functionsamePassword(password1, password2) {
returnnewPromise(function (resolve, reject) {
bcrypt.compare(password1, password2, (err, same) => {
err = err || (!same && newError("Password mismatched!"));
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
db.one(text, values)
.then(userObject => {
returnsamePassword(password, userObject.password);
})
.catch(error => {
returnnext(error, null);
});
Other than that, the promisify
approach is the way to go. But it is always good to understand what it effectively does ;)
Post a Comment for "How To Turn Nested Callback Into Promise?"