我有一条路线允许用户通过向他们发送电子邮件来重置密码。大多数网站的标准程序。在这个路由中,我导入异步npm模块并使用瀑布方法,以便我可以处理多个函数的异步性质。我仍然在理解承诺方面遇到一些麻烦,但我试图用承诺或承诺链取代瀑布。
我怎么能以承诺重构这条路线?以下是此路线中包含的步骤,目前通过瀑布分为4个功能。
返回状态200。
app.post('/forgotPassword', function(req, res, next) {
waterfall([
// generate reset token
function(done) {
crypto.randomBytes(20, function(err, buf) {
var token = buf.toString('hex');
done(err, token);
});
},
function(token, done) {
// search for user with the given email
User.findOne({ email: req.body.email }, function(err, user) {
// check to see if the user exists
if (!user) {
// user doesn't exist in database
return res.status(404).send();
}
// user exists, assign token with expiration date
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour from now
// save the user model with the newly added
// token and expiration date
user.save(function(err) {
done(err, token, user);
});
});
},
function(token, user, done) {
var smtpTransport = nodemailer.createTransport('SMTP', {
service: 'SendGrid',
auth: {
user: config.sendgridUser,
pass: config.sendgridPassword
}
});
var mailOptions = {
to: user.email,
from: 'email@school.edu',
subject: 'Password Reset',
text: `Hello etc etc`,
smtpTransport.sendMail(mailOptions, function(err) {
done(err, 'done');
});
}],
function(err) {
// handle error
if (err) return next(err);
res.status(200).send();
});
}); // end POST route '/forgotPassword'
答案 0 :(得分:3)
Promise
是一个非常强大的工具。一开始可能很难理解它,但完全值得付出努力!如果您有任何疑问,请告诉我。)
app.post('/forgotPassword', function(req, res, next)
{
new Promise((resolve, reject) =>
{
// generate reset token
crypto.randomBytes(20, (err, buf) =>
{
if(err)
return reject(err);
const token = buf.toString('hex');
resolve(token);
});
})
.then((token) =>
{
return new Promise((resolve, reject) => {
// search for user with the given email
User.findOne({ email: req.body.email }, (err, user) =>
{
if (!user)
return reject(404);
// user exists, assign token with expiration date
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour from now
// save the user model with the newly added
// token and expiration date
user.save(function(err) {
if(err)
return reject(err);
resolve(user);
});
});
});
})
.then((user) =>
{
return new Promise((resolve, reject) =>
{
const smtpTransport = nodemailer.createTransport('SMTP',
{
service: 'SendGrid',
auth: {
user: config.sendgridUser,
pass: config.sendgridPassword
}
});
const mailOptions = {
to: user.email,
from: 'email@school.edu',
subject: 'Password Reset',
text: `Hello etc etc`
};
smtpTransport.sendMail(mailOptions, (err) =>
{
if(err)
return reject(err);
resolve();
});
});
})
.then(() => res.sendStatus(200))
.catch((err) =>
{
//check if the error is the one from the DB where the user was not found
if(err == 404) {
return res.sendStatus(404);
}
return res.status(500).send(err);
});
});
答案 1 :(得分:1)
bluebird是最受欢迎的承诺库之一。 它提供了promisify函数来将回调地狱转换为承诺。
请阅读本文档并使用它。
答案 2 :(得分:1)
这个答案假设有4个完全连续的步骤:
(Function () {
Return Promise (function (resolve, promise) {
// try to do some stuff
if (success) {
Resolve ("pass this value on to next link in the chain");
}
Reject();
})()
.then (function (value) {
// do next step
Return "pass this value on to next link in the chain";
.then (function (value) {
// do next step
Return "pass this value on to next link in the chain";
.catch (function (error) {
// handle any reject or any error in the chain
}
您可以与个人.then
相关也选择返回Promise
。注意:将吞下.catch
块中的任何错误。
答案 3 :(得分:0)
这只是ES6
承诺但工作代码的示例。您可以进一步重构它以获得清晰的可重用代码。您可以使用ES6箭头功能替换ES5功能。
app.post('/forgotPassword', function(req, res, next) {
var catch = function(err){
return next(err);
}:
var error404 = function(){
return res.status(404).send();
};
var success = function(){
return res.status(200).send();
};
var step1 = new Promise(function(resolve, reject){
crypto.randomBytes(20, function(err, buf) {
if(err){
reject(err);
} else {
var token = buf.toString('hex');
resolve(token);
}
});
});
var step2 = function(token) {
// search for user with the given email
User.findOne({ email: req.body.email }, function(err, user) {
// check to see if the user exists
if (!user) {
// user doesn't exist in database
return {error404: true};
}
// user exists, assign token with expiration date
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour from now
// save the user model with the newly added
// token and expiration date
user.save(function(err) {
return { error: err, token: token, user: user });
});
});
};
var step3 = function(obj) {
if(obj.error){
return catch(obj.error);
} else if(obj.error404) {
return error404();
} else {
var token = obj.token, user = obj.user;
var smtpTransport = nodemailer.createTransport('SMTP', {
service: 'SendGrid',
auth: {
user: config.sendgridUser,
pass: config.sendgridPassword
}
});
var mailOptions = {
to: user.email,
from: 'email@school.edu',
subject: 'Password Reset',
text: `Hello etc etc`,
smtpTransport.sendMail(mailOptions, function(err) {
if(err){
return catch(err);
} else {
return success();
}
});
}
};
step1.then(step2, catch).then(step3);
}); // end POST route '/forgotPassword'
答案 4 :(得分:0)
当你正在处理已经返回方法的库时,或者像bluebird这样的库可以让你宣传现有的回调API,Prom的工作效果最好,否则就会有大量的转换。
如果您仍然使用简单的es6进行重构,请将您的promise-wrappers放在尽可能低的级别,以获得最佳效果和最干净的错误处理,基本上是手动promisify:
let convert = (resolve, reject) => (err, res) => err ? reject(err) : resolve(res);
crypto.randomBytesAsync = n=>new Promise((y,n)=>crypto.randomBytes(n,convert(y,n)));
User.findOneAsync = u => new Promise((y, n) => User.findOne(u, convert(y, n)));
User.prototype.saveAsync = () => new Promise((y, n) => this.save(convert(y, n)));
然后像这样使用它们:
app.post('/forgotPassword', function(req, res, next) {
crypto.randomBytesAsync(20).then(buf => {
var token = buf.toString('hex');
// search for user with the given email
return User.findOneAsync({ email: req.body.email }).then(user => {
// check to see if the user exists
if (!user) {
// user doesn't exist in database
res.status(404).send();
throw;
}
// user exists, assign token with expiration date
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour from now
// save the user model with the newly added
// token and expiration date
return user.saveAsync().then(() => {
var smtpTransport = nodemailer.createTransport('SMTP', {
service: 'SendGrid',
auth: { user: config.sendgridUser, pass: config.sendgridPassword }
});
smtpTransport.sendMailAsync =
o => new Promise((y, n) => smtpTransport.sendMail(o, convert(y, n)));
return smtpTransport.sendMailAsync({
to: user.email,
from: 'email@school.edu',
subject: 'Password Reset',
text: `Hello etc etc`,
});
});
})
})
.then(() => res.status(200).send())
.catch(err => next(err));
}); // end POST route '/forgotPassword'