ExpressJS / NodeJS / Promises:从承诺链早期返回

当我在服务器上发布一个请求来创build一个新的游戏时,我会执行一些查询。 首先,我search用户是否已经在游戏中,如果是,则返回游戏。 否则,我search一个开放的游戏,在这个游戏中有人正在等待对手,如果是这样的话,就返回那个游戏。 最后,如果没有发现上述状态的游戏,我创build一个新游戏并返回。 所以我的代码看起来像这样:

.post( function(req, res, next){ ...findUsersExistingGame... .then(function(game){ if(game){ return res.send(game); } else{ return ...findUserWaitingForOpponentsGame... } } .then(function(game){ if(game){ return res.send(game); } else{ return ...createNewGame... } }) .then(function(game){ return res.send(game); }) .catch(function(err){ return next(err); }); 

我最终将每个函数重构为辅助函数,以提高可读性,但是我需要首先确定链接。 我的问题是,如果我在承诺链中早期发现一个游戏(即,有一个用户的现有游戏或另一个正在等待对手的用户),则返回res.send(游戏)。 然而,第三个.then会抛出一个错误,因为我以前的.then()语句返回undefined。 如果我想做一个res.send(游戏),我如何早日退出承诺链?

选项1:我已经看到了一些build议来抛出一个错误,并明确地捕捉错误,但是这种感觉从根本上是错误的,使用错误来控制stream量。

scheme2:我可以做这样的事情,而不是链接承诺,但是类似于“promise / callback hell”:

 .post( function(req, res, next){ ...findUsersExistingGame... .then(function(game){ if(game){ return res.send(game); } else{ ...findUserWaitingForOpponentsGame... .then(function(game){ if(game){ return res.send(game); } else{ return ...createNewGame... .then(function(game){ return res.send(game); }); } }) } } 

还有另一种方法(最好在ES5中,因为我仍然试图从根本上理解承诺,但ES6也欢迎)?

这里主要的问题是你从这一步的每一步有三个可能的返回值:

  1. 发现游戏
  2. 游戏还没有find
  3. 查找游戏时出错

由于promise只能自然地分离错误而没有错误,所以只要你想要以不同的方式处理这三个单独的返回中的每一个,就要添加一些你自己的分支逻辑。

要使用承诺结果干净地进行分支,需要额外的嵌套层次,通常没有理由避免它,因为这会使您的代码最容易遵循并理解其逻辑。

 .post( function(req, res, next) { findUsersExistingGame(...).then(function(game) { if (game) return game; return findUserWaitingForOpponentsGame(...).then(function(game) { if (game) return game; // createNewGame() either resolves with a valid game or rejects with an error return createNewGame(...); }); }).then(function(game) { res.send(game); }, function(err) { // send an error response here }); }); 

请注意,这是如何简化每个阶段的回报,并返回下一个嵌套的承诺,使事情链条,并集中处理发送响应到一个地方,以减less整体代码。


现在,你可以通过让你的每个函数接受以前的游戏值来隐藏一些逻辑,并让他们检查是否已经有一个有效的游戏,如果是的话,他们什么也不做:

 .post( function(req, res, next) { findUsersExistingGame(args) .then(findUserWaitingForOpponentsGame) .then(createNewGame) .then(function(game) { res.send(game); }, function(err) { // send an error response here }); }); 

但是,在findUserWaitingForOpponentsGame()内部,您必须接受findUsersExistingGame()parsing的确切参数,并且您必须检查游戏是否有效。

 function findUserWaitingForOpponentsGame(args) { if (args.game) { return Promise.resolve(args); } else { return doAsyncFindUserWaitingForOpponentsGame(args); } } 

每个函数都可以用args对象来parsing,args对象上有任何通用参数,每个级别都可以检查.game属性。 虽然这给你一个很好的干净的控制stream程,它确实在每个函数中创build额外的代码,并强制每个函数接受前一个函数的输出(所以你可以有直链)的参数。 你可以决定你更喜欢哪一个。