Promise.catch():如何识别操作拒绝与程序性抛出之间的差异

时间:2018-03-11 22:50:49

标签: javascript node.js promise es6-promise

经过大量谷歌搜索,我找不到一个明确的例子,如何避免编程每个捕获,以确定Promise拒绝错误是编程还是操作。将此与提供回调的节点回调模式(error,params ...)进行比较,其中在error参数中干净地提供操作错误,并通过throw链处理编程错误。

请告诉我,我犯了一个noob错误,这是我错过的一个简单的答案。

修改 Node v10.0.0现在通过添加错误代码解决了这个问题。

感谢RisingStack将其发送到我的收件箱:

https://blog.risingstack.com/node-js-10-lts-feature-breakdown

......而且正式而且简洁(一如既往):

https://nodejs.org/api/errors.html#errors_error_code

考虑一个常见的例子:

function logMeIn (email, password, login_token) {
    selectFromDbByEmailAndCheckPwAndReturnId (email, password)
    .then(id => { return updateUserIdLoginToken(id, login_token); })
    .catch(error => {
        // all rejects and throws end up here
        console.log(error);
    })
})

function selectFromDbByEmailAndCheckPwAndReturnId (email, password) {
   return new Promise((resolve, reject) => {
      db.sql.query( /* params */, (error, results) => {
          blarg = 1; // <-- reference error, programmatic
          // do your SELECT * FROM Users where email=? ... etc.
          if (error) {
               return reject(error); // <-- operational sql error
          :
          :
          if (resultsRowsFromQuery.length === 0) {
             // vvvvv operational error: user not found
             return reject(new Error("User not in database"));
          }
          :
          // hash password & salt, etc etc etc ...
          :
          return resolve(resultRowsFromQuery[0].id);
      });
   });
}
// no need to code out updateUserIdLoginToken...

在这个示例中,catch将捕获编程错误和两个操作错误,并且我必须编程catch以确定哪个。如果我想向用户返回未找到他们的电子邮件的事实,我不能仅使用该消息,因为我可能会意外地返回参考错误消息。 (尴尬!)

但是,与sql.query模式进行比较,很明显错误是可操作的,因为如果blarg=1不在承诺中,function logMeIn (email, password, login_token) { try { selectFromDbByEmailAndCheckPwAndReturnId (email, password, (error, id) => { if (error) { console.log("Operational error:", error) return; } // no error, got id, do next step... updateUserIdLoginToken(id, login_token, error => { // do next thing, like return res.render() or something... }); }); } catch (e) { console.error("Programmatic error:", e); } }) function selectFromDbByEmailAndCheckPwAndReturnId (email, password, callback) { db.sql.query( /* params */, (error, results) => { blarg = 1; // <-- reference error, programmatic // do your SELECT * FROM Users where email=? ... etc. if (error) { return callback(error, null); } : : if (resultsRowsFromQuery.length === 0) { // vvvvv operational error: user not found return callback(new Error("User not in database"), null); } : // hash password & salt, etc etc etc ... : return callback(null, id); }); } 会冒泡到更高的级别。

我看到很少有关于拒绝值应该是什么以及如何区分的文档。我考虑过使用resolve(new Error()),以便我的成功履行功能确定是否存在操作错误,并且为程序错误保存.catch,但这只是愚蠢。

那里有很多不良信息因为它在过去的7年里经常引用bluebird,Q,A +和ES6 ......很难找到ES6 Node / 7/9的例子...... [I&# 39;甚至看过声称使用.then(func A(),func B())的链接.catch()会将编程错误发送给B而不是catch()。 LOL。]

思想?

编辑#1:请求无承诺的例子:

DX

4 个答案:

答案 0 :(得分:4)

在基于回调的代码中,您必须自己处理错误,并在必要时抛出错误。异步调用不会简单地抛出错误。 现在,如果你想要实现的承诺方式,当然唯一的方法就是将错误视为成功,然后在“then”链中处理它...而不是在catch链中。这是确定是否有任何错误的唯一方法。 但是,正如您所知,在promises中,您只能解析一个数据而不是逗号分隔的数据列表。 所以,你应该遵循一个标准,正如传统的回调所说的那样,第一个参数将是错误,如果有的话,跟随将是响应。

根据你的例子:

function logMeIn (email, password, login_token) {
    selectFromDbByEmailAndCheckPwAndReturnId (email, password)
    .then(response => { 
      if(response.error) {
        // Operational error
      } else {
        // successful response
      }
    })
    .catch(error => {
        // programmatic errors;
        console.log(error);
    })
})

function selectFromDbByEmailAndCheckPwAndReturnId (email, password) {
   return new Promise((resolve, reject) => {
      db.sql.query( /* params */, (error, results) => {
          blarg = 1; // <-- reference error, programmatic
          // do your SELECT * FROM Users where email=? ... etc.
          if (error) {
               return resolve({ error }); // <-- operational sql error
          :
          :
          if (resultsRowsFromQuery.length === 0) {
             // vvvvv operational error: user not found
             return resolve({ error: new Error("User not in database") });
          }
          :
          // hash password & salt, etc etc etc ...
          :
          return resolve({ result: resultRowsFromQuery[0].id });
      });
   });
}

答案 1 :(得分:3)

您对节点样式和基于承诺的代码的期望过高。这两种异步函数都没有区分操作错误和程序错误的概念,你可以直接抛出/拒绝任何东西,这就是为什么你没有找到关于它的文档。这两种模式都是异步代码流的原语,仅此而已。节点式版本有点尴尬,因为它允许传播同步和异步错误(您需要try-catchif(error)来处理所有错误)。虽然他们应该只使用异步版本。使用两个&#34;错误通道&#34;在一个功能中不是一个功能,它只是行为不端的代码。

节点样式和基于承诺的异步代码都不应抛出常规的同步错误。因此,请不要使用这两种不同的错误传播通道来区分编程错误和操作错误。

所以回答这个问题,你如何区分它们?就像使用常规同步代码一样,您必须引入自己的抽象:

  • 要么使每个服务函数返回某种结果类型,该类型将包含操作错误字段(请参阅生锈错误处理:https://doc.rust-lang.org/book/first-edition/error-handling.html
  • 或创建一个OperationalError类,根据需要使用尽可能多的子类,并使您的顶级代码区分OperationalError - s和任何其他类型的错误。这就是我的建议。
  • 或使用您的框架提供的内容,虽然我没有找到任何好的示例

答案 2 :(得分:2)

  

我找不到一个明确的例子,如何避免编程每个捕获,以确定Promise拒绝错误是编程还是可操作。

这是因为没有一个。在标准和设计中,承诺处理必须通过在{/ p>的调用周围放置try/catch块来捕获程序错误

  • 承诺执行者职能,
  • then.catch在承诺(onFulfilledorRejected处理程序)上注册的回调,以及
  • 调用在不同代码库中创建的类似承诺的对象的then方法

如果发现错误,请拒绝正在构建的承诺,或thencatch返回的承诺错误。没有蠕动的空间。

有一些期望在一个完美的世界开发人员会在将代码投入生产之前读取错误消息并调试程序错误。

因此,在未经测试的代码中,您需要在执行程序和处理程序代码中重新插入try/catch语句以捕获程序错误。 catch按照您的意愿捕获进程错误,如果您因同样的原因拒绝承诺,则再次抛出它。

你也可以在这样的catch块中抛出你自己的特殊对象,用于编程错误,这些错误可以由承诺链中的.catch处理程序进行测试和检测。

答案 3 :(得分:0)

Node JS没有提供任何内置机制来执行此操作。但是可以通过检查catch语句中错误对象的'type'参数来手动执行此操作。

如果编程错误,类型中的值将是EvalError,RangeError,ReferenceError,SyntaxError,TypeErro或URIError之一。

如果您不想手动执行此操作,请查看bounce npm模块。