如何在 Express 中修复这个 ES6 承诺链?

Posted

技术标签:

【中文标题】如何在 Express 中修复这个 ES6 承诺链?【英文标题】:How can I fix this ES6 promise chain in Express? 【发布时间】:2017-09-25 10:10:07 【问题描述】:

我从 Promise 链接开始,我正在尝试在 node.js 中使用 express 和 mongoose 的一些代码。这是我的代码:

const register = (req, res, next) => 
  User.findOne(req.params.email).exec().then(user => 
    if (user) 
      return res.status(409).json( error: 409 );
    
    return User.create(req.body);
  ).then(user => 
    const token = jwt.sign( sub: user._id , 'test-secret');
    return res.status(200).json( token );
  ).catch(err => 
    return next(err);
  );
;

这是注册用户并向他发送令牌的简化代码。我要做的是,首先检查用户是否已经注册,如果没有,请注册。

如您所见,我认为第 6 行是错误的,因为我没有返回任何 Promise,所以在第 4 行之后,代码继续执行。我想避免回调地狱,我该如何做到这一点?谢谢

【问题讨论】:

【参考方案1】:

您没有返回Promise 这一事实并不重要。 then()catch() 的返回值会自动包装在 Promise 对象中。

问题是您的流程是线性的(它遵循直线路径),但您正在尝试分支以便某些部分仅在某些情况下运行。你需要两条执行路径:

const register = (req, res, next) => 
  User.findOne(req.params.email).exec().then(user => 
    if (user) 
      // Path 1 stops here:
      return res.status(409).json( error: 409 );

     else 
      // Path 2 continues down this 2nd Promise chain:
      return User.create(req.body).then(user => 
        const token = jwt.sign( sub: user._id , 'test-secret');
        return res.status(200).json( token );
      )
    

  ).catch(err => 
    // Both paths converge on this error handler
    return next(err);
  );
;

如果你想避免深度嵌套,你有两种选择:

    将不同的路径封装成函数,这样你就可以这样做:

    if (user) 
      return sendHttpError(res, 409)
     else 
      return sendNewUser(res, req.body)
    
    

也许只有sendHttpError函数是必需的,其他可能的路径可以是你路由的主体。这是相当标准的。

    由于您的一个分支仅处理错误情况,您可以抛出Error 并在下面捕获它,或者在错误处理中间件中捕获它。它看起来像这样:

    if (user) 
      // This throw will abort execution of everything that follows. By
      // using a custom Error class, you can then handle it appropriately
      // in a catch() handler or Express middleware:
      throw new APIError(409)
    
    
    // ... create user and proceed normally
    

这也是一种很常见的模式。

【讨论】:

那么,这里不可能有 Promise 链吧? 从逻辑上讲,您需要一个分叉链,但不一定嵌套在代码的书面文本中。我添加了两种可能的策略来避免在答案中嵌套回调。【参考方案2】:

如果用户已经存在,只需抛出一个error 即可直接访问catch 函数。

const register = (req, res, next) => 
  User.findOne(req.params.email).exec().then(user => 
    if (user) 
      throw new Error( error: 409, msg: 'User already exists' );
    
    return User.create(req.body);
  ).then(user => 
    const token = jwt.sign( sub: user._id , 'test-secret');
    return res.status(200).json( token );
  ).catch(err => 
    return next(err);
  );
;

【讨论】:

是的,这是一个可能的答案,但我忘了说我只使用 catch 块来捕获运行时错误 您可以指定要抛出的错误,以便将运行时错误与您的错误区分开来。 throw new Error( foo: 'bar', ...);catch(err => if (err.foo === 'bar') // this is your error else // runtime error )

以上是关于如何在 Express 中修复这个 ES6 承诺链?的主要内容,如果未能解决你的问题,请参考以下文章

如何修复“仅在 ES6 中可用”的 linter 错误?

如何处理错误然后立即脱离承诺链?

打破承诺链 - 停止执行下一个“then”

原生 ES6 承诺中 Bluebird Promise.finally 的等价物是啥? [复制]

如何让 ES6 生成器等待承诺,就像在 redux-saga 中一样?

如何正确地打破承诺链?