【问题标题】:try catch vs promise chaining mechanism for error handing which one offers more flexibility?try catch 与 promise 链式错误处理机制,哪一个提供更大的灵活性?
【发布时间】:2022-01-21 09:36:24
【问题描述】:

我有一个寄存器控制器,它使用try {} catch(e) {} 机制对我来说这是一个相当不错的临时错误机制,但总的来说,它更好地切换到承诺,因为我认为承诺提供了更细粒度的错误处理机制,所以下面是我的代码截至目前在我的控制器中。

const registerNewUser = async (req, res) => {
  /*
     code here for extracting info from req
  */
  try {
    const users_data = await users.create_new_user({
      email,
      crypted_pwd,
      salt,
      first_name,
      phone_code,
    });

    const { id: user_id, first_name: user_first_name } = users_data;

    const customer_data = await customers.create_new_customer({
      first_name,
      last_name,
      email,
    });

    const {
      tenant_id,
      id: customer_id,
    } = customer_data;

    await clearCartFromRedis(merchant_canonical_name, req.token);

    signUpWelcomeMailer(merchant_canonical_name, req.token, user_id);

    return res.status(200).json({
      success: true,
      access_token: req.token,
      first_name: user_first_name,
    });
  } catch (error) {
    logger.log({ message: error.message, level: 'error' });
    return res.status(500).send(error.message);
  }
};

如您所见,在 try 块中发生了很多事情。 IE。查询到create_new_usercreate_new_customer,然后是clearCartFromRedis,然后是signUpWelcomeMailer。所有这三个都可能引发不同类型的错误,但我已经在一个 catch 块中处理了它们,其中我有以下语句。

return res.status(500).send(error.message);

所以现在我的问题是使用 then()catch() 在承诺链中处理这个问题会更好吗?所以说将上面的内容重构为类似

customers.create_new_customer({
  first_name,
  last_name,
  email,
}).then(data => {
  const obj = { tenant_id: data.tenant_id, id: data.customer_id, merchant_canonical_name: data.merchant_canonical_name }
  return obj
}).then((obj) => { 
  clearCartFromRedis()
    .catch(() => { throw new Error('Could not clear cache') })
}).then(() => {
  signUpWelcomeMailer(merchant_canonical_name, req.token, user_id)
    .catch(() => { throw new Error('Error encountered while sending the email') })
}).catch(error => res.sendStatus(500))

因此,您可以看到最后有单独的错误处理以及全局 catch 块。我的问题是在我的用例中是全局try ... catch 更好还是promise 链接机制?

附: HERE 也有类似的问题,但这个问题更多地涉及try .. catchpromise chaining mechanism

【问题讨论】:

  • 您可以将.catch() 调用(用于细粒度的错误处理)与await 语法(用于简单的顺序代码)结合起来,这不是非此即彼的。
  • @Bergi 你的意思是粗略地说在try ..catch 内添加链接,并让全局错误处理由try catch 处理?
  • 是的——顺便说一句,我昨天刚刚写了an answer demonstrating this。尽管“全局错误处理”可能通常发生在函数之外,但您只需让异常向上传播
  • @Bergi,非常感谢,kidda 有帮助,所以我假设我的第一个代码块(我已经在使用 await)中唯一缺少的是 catch 部分.另外,当您说let the exceptions propagate upwards 时,您的意思是catch 块内的throw Error 之类的东西,然后在全局app.use(err => if(error) {return res.sendStatus(500)}) 中处理错误,我说得对吗?抱歉有点太好奇了:P

标签: javascript node.js error-handling


【解决方案1】:

首先,try... catch 可用于捕获大部分 expected 错误(当您需要返回一些带有附加信息的 4xxx 错误,甚至是带有业务逻辑错误代码或类似内容的 200 时),而不是一些应导致 500 个 HTTP 错误。也就是说,您需要一个全局处理程序来处理所有未捕获/意外错误,该处理程序将记录这些错误并将 500 错误发送给客户端。

全局错误处理程序

  app.use(function (err, req, res, next) {
    logger.error(err)

    res.status(500).send('Unexpected error')
  })

尝试/捕获显式事务

const registerNewUser = async (req, res) => {
  /*
     code here for extracting info from req
  */
  // here is some call to create a transaction in a DB
  // it varies depending on a type of DB and a package you use to communicate with it
  // we need to create a transaction BEFORE try/catch block
  const transaction = await createTransaction();
  try {
    // we need to pass transaction to every method that modifies data in DB
    // also depends on how to pass a transaction to underlying DB package methods
    const users_data = await users.create_new_user({
      email,
      crypted_pwd,
      salt,
      first_name,
      phone_code,
    }, transaction);

    const { id: user_id, first_name: user_first_name } = users_data;

    // we need to pass transaction to every method that modifies data in DB
    const customer_data = await customers.create_new_customer({
      first_name,
      last_name,
      email,
    }, transaction);

    const {
      tenant_id,
      id: customer_id,
    } = customer_data;

    await clearCartFromRedis(merchant_canonical_name, req.token);

    signUpWelcomeMailer(merchant_canonical_name, req.token, user_id);

    // here we need to commit the transaction before we exit from this handler
    await transaction.commit();

    return res.status(200).json({
      success: true,
      access_token: req.token,
      first_name: user_first_name,
    });
  } catch (error) {
    // here we need to rollback the transaction before we exit from this handler
    await transaction.rollback();
    logger.log({ message: error.message, level: 'error' });
    return res.status(500).send(error.message);
  }
};

其次,在您对某些数据库进行多次操作的情况下,您显然需要使用事务机制来保持数据一致。通常,它需要创建一个显式事务并将其传递给应该作为一个原子操作执行的每个查询,该操作要么正确执行(然后您需要提交事务),要么失败(然后您需要回滚事务)。为此,您可以使用try...catch 在一批操作结束时提交或回滚事务。

【讨论】:

  • 谢谢你第二部分的回答真的很有见地。第一部分更令人困惑,在我提供的示例中,try...catch 如何比承诺链机制更好?能举个例子吗?
  • 添加到答案中
  • 非常感谢您的麻烦,尤其是您提到的附加点:D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-25
  • 2018-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-28
  • 2021-08-08
相关资源
最近更新 更多