处理承诺链中的多个捕获

我仍然是相当新的承诺,目前正在使用蓝鸟,但我有一个情况,我不太清楚如何最好地处理它。

因此,例如我有一个快速应用程序中的承诺链如下所示:

repository.Query(getAccountByIdQuery) .catch(function(error){ res.status(404).send({ error: "No account found with this Id" }); }) .then(convertDocumentToModel) .then(verifyOldPassword) .catch(function(error) { res.status(406).send({ OldPassword: error }); }) .then(changePassword) .then(function(){ res.status(200).send(); }) .catch(function(error){ console.log(error); res.status(500).send({ error: "Unable to change password" }); }); 

所以我后面的行为是:

  • 去Id获得帐户
  • 如果在这一点上被拒绝,就会炸毁并返回一个错误
  • 如果没有错误,则将返回的文档转换为模型
  • validation数据库文档的密码
  • 如果密码不匹配,然后炸出来,并返回一个不同的错误
  • 如果没有错误更改密码
  • 然后返回成功
  • 如果还有其他问题,请退回500

所以目前的捕获似乎并没有停止链接,这是有道理的,所以我想知道是否有一种方法让我以某种方式迫使链条停止在基于错误的某个点上,或者如果有更好的方法构造这个以获得某种forms的分支行为,因为存在if X do Y else Z

任何帮助将是伟大的。

这种行为完全像一个同步抛出:

 try{ throw new Error(); } catch(e){ // handle } // this code will run, since you recovered from the error! 

这是.catch的一半 – 能够从错误中恢复。 重新投射以表示状态仍然是一个错误:

 try{ throw new Error(); } catch(e){ // handle throw e; // or a wrapper over e so we know it wasn't handled } // this code will not run 

但是,这一点在你的情况下是行不通的,因为错误被后来的处理程序所捕获。 这里真正的问题是广义的“HANDLE ANYTHING”error handling器一般是不好的做法,在其他编程语言和生态系统中是极其不被接受的。 出于这个原因,蓝鸟提供了键入和谓词捕获。

额外的好处是您的业务逻辑不(必须)不必知道请求/响应周期。 查询的责任不在于确定客户端获取哪个HTTP状态和错误,随着应用程序的增长,您可能需要将发送给客户端的业务逻辑(如何查询数据库以及如何处理数据)分开(什么http状态码,什么文本和什么响应)。

这是我如何写你的代码。

首先,我会让.Query抛出一个NoSuchAccountError ,我从Bluebird已经提供的Promise.OperationalError子类化它。 如果您不确定如何子类错误让我知道。

我另外将其子类化为AuthenticationError ,然后执行如下操作:

 function changePassword(queryDataEtc){ return repository.Query(getAccountByIdQuery) .then(convertDocumentToModel) .then(verifyOldPassword) .then(changePassword); } 

正如你所看到的那样 – 它非常干净,你可以阅读文本,就像在这个过程中发生的事情一样。 它也是从请求/响应中分离出来的。

现在,我会从路由处理程序中调用它:

  changePassword(params). catch(NoSuchAccountError, function(e){ res.status(404).send({ error: "No account found with this Id" }); }).catch(AuthenticationError, function(e){ res.status(406).send({ OldPassword: error }); }).error(function(e){ // catches any remaining operational errors res.status(500).send({ error: "Unable to change password" }); }).catch(function(e){ res.status(500).send({ error: "Unknown internal server error" }); }); 

这样,逻辑全在一个地方,如何处理错误的决定是在一个地方,他们不混乱海誓山盟。

.catchtry-catch语句一样工作,这意味着最后只需要一个catch:

 repository.Query(getAccountByIdQuery) .then(convertDocumentToModel) .then(verifyOldPassword) .then(changePassword) .then(function(){ res.status(200).send(); }) .catch(function(error) { if (/*see if error is not found error*/) { res.status(404).send({ error: "No account found with this Id" }); } else if (/*see if error is verification error*/) { res.status(406).send({ OldPassword: error }); } else { console.log(error); res.status(500).send({ error: "Unable to change password" }); } }); 

我想知道是否有办法让我以某种方式迫使这个链条在错误的某个点上停下来

不,你不能真的“结束”一个链,除非你抛出一个exception,直到结束。 见本杰明Gruenbaum的答案是如何做到这一点。

他的模式的派生将不是区分错误types,而是使用可以从单个通用的.catch处理程序发送的具有statusCodebody字段的错误。 根据你的应用程序结构,他的解决scheme可能会更清洁。

或者如果有更好的方法来构造这个来获得某种forms的分支行为

是的,你可以做承诺分支 。 但是,这意味着要离开链并“回到”嵌套 – 就像在嵌套的if-else或try-catch语句中一样:

 repository.Query(getAccountByIdQuery) .then(function(account) { return convertDocumentToModel(account) .then(verifyOldPassword) .then(function(verification) { return changePassword(verification) .then(function() { res.status(200).send(); }) }, function(verificationError) { res.status(406).send({ OldPassword: error }); }) }, function(accountError){ res.status(404).send({ error: "No account found with this Id" }); }) .catch(function(error){ console.log(error); res.status(500).send({ error: "Unable to change password" }); });