草庐IT

javascript - promise .catch() : how to identify the differences between operational rejects and programmatical throws

coder 2025-03-04 原文

经过大量谷歌搜索后,我无法找到一个明确的示例,说明如何避免对每个 catch 进行编程以确定 Promise 拒绝错误是程序性错误还是操作性错误。将此与提供 callback(error, params...) 的 Node 回调模式进行比较,在 error 参数中明确提供操作错误,并通过抛出链处理编程错误。

请告诉我我犯了一个菜鸟错误,对此我错过了一个简单的答案。


编辑 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 不在 promise 中,它会冒泡到更高的级别。

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

那里有很多不好的信息,因为它在过去 7 年里经常引用 bluebird、Q、A+ 和 ES6……很难找到 ES6 Node/7/9 的例子……[我什至见过声称使用 .then(func A(), func B()).catch() 的链接会将编程错误发送给 B 而不是 catch()。哈哈。]

想法?

编辑 #1:请求无 promise 示例:

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);
  });
}

最佳答案

您对 Node 式代码和基于 promise 的代码期望过高。两种异步函数都无法区分操作错误和编程错误的概念,您可以从字面上抛出/拒绝任何东西,这就是为什么您没有找到太多关于它的文档的原因。这两种模式都是异步代码流的原语,仅此而已。 Node 式版本有点笨拙,因为它允许传播同步和异步错误(您需要 try-catchif(error) 来处理所有错误)。尽管他们应该只使用异步版本。在单个函数中使用两个“错误 channel ”不是一个功能,它只是行为不端的代码。

Node 样式和基于 promise 的异步代码都不应抛出常规同步错误。因此不要使用这两种不同的错误传播 channel 来区分编程错误和操作错误。

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

  • 要么让每个服务函数返回某种结果类型,该类型将有一个用于操作错误的字段(参见 rust 的错误处理:https://doc.rust-lang.org/book/first-edition/error-handling.html)
  • 或创建一个 OperationalError 类,使用任意数量的子类,并使您的顶级代码区分 OperationalError-s 和任何其他类型的错误。这是我的建议。
  • 或者使用你的框架提供的,虽然我没有找到任何好的例子

关于javascript - promise .catch() : how to identify the differences between operational rejects and programmatical throws,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49225968/

有关javascript - promise .catch() : how to identify the differences between operational rejects and programmatical throws的更多相关文章

随机推荐