Récemment, j’ai commencé à utiliser pg-promise avec la bibliothèque bluebird. J’ai toujours nested callback et manipulé des err
dans chaque callback. Je trouve que la déclaration de catch
promis semble vraiment soignée. Je ne suis pas sûr s’il est possible de transformer ce code pour promettre base?
username = username.toUpperCase(); let text = "SELECT * FROM users WHERE username = $1"; let values = [username]; database.one(text, values).then(function (userObject) { // ANY WAY TO TURN this nested bycrypt into promise chain? bcrypt.compare(password, userObject.password, function (err, same) { if (err) { return next(err, null); } if (!same) { return next(new Error("Password mismatched!"), null); } const serializeObject = {_id: userObject._id}; return next(null, serializeObject); }); }).catch(function (err) { return next(err, null); });
J’imagine qu’en utilisant bluebirds promisify, vous prometsortingez bcrypt.compare comme ça (vous n’avez pas à utiliser la partie async du nom)
let compareAsync = Promise.promisify(bcrypt.compare);
parce que
userObject
dans le premier fichier .then doit être utilisé dans le second. Ensuite, vous ne pouvez pas simplement chaîner les fichiers .then en retournant compareAsync, car le prochain .then n’aura pas access àuserObject
un correctif, consiste à utiliser une variable qui sera dans la scope des deux .then (mais pouah)
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; return compareAsync(password, userObject.password); }).then(function(same) { if (!same) { throw new Error("Password mismatched!"); } const serializeObject = {_id: uo._id}; return next(null, serializeObject); }).catch(function (err) { return next(err, null); });
une autre option (à mon avis plus propre) est un nested .then
username = username.toUpperCase(); let text = "SELECT * FROM users WHERE username = $1"; let values = [username]; database.one(text, values).then(function (userObject) { return compareAsync(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) { throw new Error("Password mismatched!"); } let serializeObject = { _id: userObject._id }; return next(null, serializeObject); }).catch(function (err) { return next(err, null); });
REMARQUE: bluebird a une fonction promisifyAll … qui promet des fonctions dans un object et ajoute (par défaut) le suffixe Async
au nom de la fonction. Je pense que vous pouvez choisir un nom postfix différent, mais la documentation vous en dira plus.
lorsque vous promettez une seule fonction, vous déclarez le nom vous-même – ce qui précède aurait facilement pu être
let trumpIsBigly = Promise.promisify(bcrypt.compare);
alors vous utiliseriez simplement trumpIsBigly
où le code a compareAsync
Une dernière possibilité
CompareAsync promisified roulé à la main (principalement tiré de la réponse de vitaly-t mais avec des ajouts)
function compareAsync(password1, password2, inValue) { return new Promise(function (resolve, reject) { bcrypt.compare(password1, password2, function (err, same) { err = err || (!same && new Error("Password mismatched!")); if (err) { reject(err); } else { resolve(inValue); } }); }); }
Maintenant, compareAsync ne résoudra la valeur entrante inValue
que s’il n’y a pas d’erreur, et que c’est pareil
username = username.toUpperCase(); let text = "SELECT * FROM users WHERE username = $1"; let values = [username]; database.one(text, values).then(function (userObject) { return compareAsync(password, userObject.password, userObject) }).then(function (userObject) { let serializeObject = { _id: userObject._id }; return next(null, serializeObject); }).catch(function (err) { return next(err, null); });
Ce qui rend la “chaîne” très simple!
Ceci va s’étendre à la réponse de @ Jaromanda, au cas où vous n’utiliseriez que cette fonction et que vous souhaitiez simplement voir comment la promulguer manuellement.
function samePassword(password1, password2) { return new Promise(function (resolve, reject) { bcrypt.compare(password1, password2, (err, same) => { err = err || (!same && new Error("Password mismatched!")); if (err) { reject(err); } else { resolve(); } }); }); } db.one(text, values) .then(userObject => { return samePassword(password, userObject.password); }) .catch(error => { return next(error, null); });
Autre que cela, l’approche promisify
est la voie à suivre. Mais il est toujours bon de comprendre ce qu’il fait effectivement;)