节点promise-chaining导致函数中的返回语句太多

时间:2016-02-13 08:16:27

标签: node.js promise

假设下一个重置密码功能:

function forgotPassword(email){
  return Promise.resolve().then(function() {
    return User.findByMail(email);
  }).then(function(user){
    if (!user) {
      return Promise.reject({message: 'Cannot find user with that email'});
    }

    return [user, tokensService.createRandomBytes()];
 }).spread(function(user, token){
    user.resetPasswordToken = token;
    user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
    return [user.saveAsync(), token];
  }).spread(function(user, token){
    return emailService.sendResetPassword(user.email, token);
  });
}

此函数的调用者期望将promise作为返回值。

但我的问题是: 这是连锁承诺的正确方法吗?

在我看来,所有返回语句都会使代码看起来不可读。

有没有办法避免这种情况?有没有办法在开始时避免return Promise.resolve().then...

PS。我使用bluebird作为承诺lib

更新:

在Dans回答后,我做了以下事情:

function login(email, password) {
  return User.findByMail(email).then(function(user){
    return [user.comparePassword(password), user];
  })
}

我一直得到Undefined不是一个函数,这里是堆栈跟踪:

TypeError: undefined is not a function
at Object.login (/home/royi/projects/travessey-api/src/authentication/authentication-controller.js:17:11)
at /home/royi/projects/travessey-api/src/authentication/authentication-router.js:8:18
at Layer.handle [as handle_request] (/home/royi/projects/travessey-api/node_modules/express/lib/router/layer.js:95:5)
at next (/home/royi/projects/travessey-api/node_modules/express/lib/router/route.js:131:13)
at Route.dispatch (/home/royi/projects/travessey-api/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/home/royi/projects/travessey-api/node_modules/express/lib/router/layer.js:95:5)
at /home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:277:22
at Function.process_params (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:330:12)
at next (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:271:10)
at Function.handle (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:176:3)
at router (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:46:12)
at Layer.handle [as handle_request] (/home/royi/projects/travessey-api/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:312:13)
at /home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:280:7
at Function.process_params (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:330:12)
at next (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:271:10)
at allowCrossDomains (/home/royi/projects/travessey-api/src/authentication/authentication-middleware.js:34:5)
at Layer.handle [as handle_request] (/home/royi/projects/travessey-api/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:312:13)
at /home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:280:7
at Function.process_params (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:330:12)
at next (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:271:10)
at /home/royi/projects/travessey-api/node_modules/express-validator/lib/express_validator.js:228:5
at Layer.handle [as handle_request] (/home/royi/projects/travessey-api/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:312:13)
at /home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:280:7
at Function.process_params (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:330:12)
at next (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:271:10)
at urlencodedParser (/home/royi/projects/travessey-api/node_modules/body-parser/lib/types/urlencoded.js:81:44)
at Layer.handle [as handle_request] (/home/royi/projects/travessey-api/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:312:13)
at /home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:280:7
at Function.process_params (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:330:12)
at next (/home/royi/projects/travessey-api/node_modules/express/lib/router/index.js:271:10)

3 个答案:

答案 0 :(得分:2)

  

在我看来,所有的返回语句都会使代码看起来不可读。有没有办法避免这种情况?

实际上有两种方式,但它们都不适用于ES5:

  • 如果您只有一个表达式,则箭头函数不需要显式return

    const forgotPassword = (email) => 
      Promise.resolve().then(() =>
        User.findByMail(email)
      ).then(user =>
        user
          ? [user, tokensService.createRandomBytes()]
          : Promise.reject({message: 'Cannot find user with that email'})
      ).spread((user, token) => {
        user.resetPasswordToken = token;
        user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
        return [user.saveAsync(), token];
      }).spread((user, token) => 
        emailService.sendResetPassword(user.email, token);
      );
    
  • 异步功能允许您完全躲避then并使用await关键字简化所有内容:

    async function forgotPassword(email) {
      await Promise.resolve();
      let user = await User.findByMail(email);
      if (!user)
        throw new Error('Cannot find user with that email');
    
      let token = tokensService.createRandomBytes();
      user.resetPasswordToken = token;
      user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
      user = await user.saveAsync();
      return emailService.sendResetPassword(user.email, token);
    }
    

    它们是ES8提议的功能,但您已经可以在浏览器中使用它们了。使用Bluebird,您也可以类似地使用生成器,请参阅Promise.coroutine docs

  

有没有办法避免返回Promise.resolve()。然后......一开始?

是。您可以使用第一个承诺返回功能启动您的链:

function forgotPassword(email){
  return User.findByMail(email).then(function(user){
    if (!user) {
      …

如果您不确定是否返回承诺,您也可以使用

function forgotPassword(email){
  return Promise.resolve(User.findByMail(email)).then(function(user){
    if (!user) {
      …

当前解决方案的差异(异常处理,异步)非常小。

答案 1 :(得分:1)

User.findByMail(email)导致值而非承诺时,以下情况不属实:

  

您可以轻松删除第一个:

return Promise.resolve().then(function() {
  return User.findByMail(email);
})
     

与:

相同
return User.findByMail(email)

因为它没有:

return Promise.resolve(User.findByMail(email))

将该值包装为承诺。

答案 2 :(得分:0)

你不必为了链接而链:

function forgotPassword(email) {
  return Promise.all([
    User.findByMail(email),
    // creating an unrelated random token doesn't have to be done later
    tokensService.createRandomBytes()
  ]).spread(function(user, token) {
    if (!user) {
      // equivalent to your Promise.reject() but cleaner
      // and err.message == 'Cannot find user with that email'
      throw new Error('Cannot find user with that email');
    }

    user.resetPasswordToken = token;
    user.resetPasswordExpires = Date.now() + 3600000; // 1 hour

    // nothing wrong with one level of nesting
    return user.save().then(function(usr) {
      return emailService.sendResetPassword(user.email, token);
    })
  });
}