处理从回调函数中抛出的错误的正确方法是什么?

What is the proper way of handling errors that are thrown from withing a callback function?

我正在开发一个 NodeJS 应用程序,我正在使用 mongoose 将数据保存到我的 MongoDB 数据库中。

我的控制器可以在 /register url 处接受一个 POST 请求和一些数据。看起来像这样:

router.post("/register", async (req: Request, res: Response) => {
  const accountModel: IRegistrationAccount = {
    firstName: req.body.firstName,
    lastName: req.body.lastName,
    email: req.body.email,
    password: req.body.password,
    repeatedPassword: req.body.repeatedPassword,
  };

  try {
    registerAccount(accountModel);
    res.status(OK).send("Registration successful.");
  } catch (err) {
    res.status(NOT_ACCEPTABLE).send(err);
  }
});

如您所见,我想 return 向用户发送一条错误消息,以便他们确切知道出了什么问题。这是 registerAccount 方法:

export function registerAccount(accountModel: IRegistrationAccount) {
  if (accountModel.firstName.length === 0)
    throw "Your first name may not be empty.";

  if (accountModel.email.length < 3) throw "Your email is too short.";

  if (accountModel.password !== accountModel.repeatedPassword)
    throw "The passwords You entered don't match.";

  if (accountModel.password.length < 8) throw "Your password is too short.";

  const account = new Account(accountModel);
  account.save(function (err) {
    if (err) return logger.err(err);

    return logger.info("Created account.");
  });
}

当用户输入的数据有问题时,我 return 使用 throw 发出错误消息,然后在控制器中捕获。问题是:我如何知道 save 中的回调函数是否抛出错误以及如何处理该错误?这是我第一次使用 Node,我尝试四处搜索但找不到合适的答案。

我会用 util.promisify 承诺 account,然后 return Promise 和 .catch 它在调用者中:

return accountSavePromisified().then(() => {
  logger.info("Created account.");
});
try {
    registerAccount(accountModel)
        .then(() => {
            res.status(OK).send("Registration successful.");
        })
        .catch((err) => {
            // Catch asynchronous errors (thrown by `.save`):
            res.status(NOT_ACCEPTABLE).send(err);
        })
} catch (err) {
    // Catch synchronous errors (thrown by your validator):
    res.status(NOT_ACCEPTABLE).send(err);
}

如果您不关心区分 .save 抛出的错误和验证器抛出的错误,您也可以 await 调用 registerAccount 而不是调用 .then 就可以了。

您还可以考虑通过 returning 来自 registerAccount 的错误字符串而不是抛出,例如 return 'Your first name may not be empty.':

const result = registerAccount(accountModel);
if (typeof result === 'string') {
  res.status(NOT_ACCEPTABLE).send(result);
  return;
}
result.then(() => {
  res.status(OK).send("Registration successful.");
})
.catch((err) => {
  res.status(NOT_ACCEPTABLE).send(result);
});

save 方法可以 return Promise 所以你根本不需要使用回调:

export async function registerAccount(accountModel: IRegistrationAccount) {
  if (accountModel.firstName.length === 0)
    throw "Your first name may not be empty.";

  if (accountModel.email.length < 3) throw "Your email is too short.";

  if (accountModel.password !== accountModel.repeatedPassword)
    throw "The passwords You entered don't match.";

  if (accountModel.password.length < 8) throw "Your password is too short.";

  const account = new Account(accountModel);
  await account.save();
}

并在调用此函数的行添加 await

try {
    await registerAccount(accountModel);
    logger.info("Created account.")
    res.status(OK).send("Registration successful.");
  } catch (err) {
    logger.err(err)
    res.status(NOT_ACCEPTABLE).send(err);
  }