async /等待正确的错误处理

时间:2018-01-27 12:53:31

标签: javascript node.js error-handling async-await sequelize.js

假设我们有一个在用户登录(express,node)上运行的动作。 这是有效的代码,使用大量回调编写:

checkIfEmailAndPasswordAreSet(email, password, (error, response) => {
  if (error) return errorResponse(403, 'validation error', error)
  findUserByEmail(email, (error, user) => {
    if (error) return errorResponse(500, 'db error', error)
    if (!user) return errorResponse(403, 'user not found')
    checkUserPassword(user, password, (error, result) => {
      if (error) return errorResponse(500, 'bcrypt error', error)
      if (!result) return errorResponse(403, 'incorrect password')
      updateUserLastLoggedIn(user, (error, response) => {
        if (error) return errorResponse(500, 'db error', error)      
        generateSessionToken(user, (error, token) => {
          if (error) return errorResponse(500, 'jwt error', error)
          return successResponse(user, token)
        })
      })
    })
  })
})

我想使用async / await重写此代码并避免回调地狱。怎么做?

第一次尝试可能看起来像这样:

try {
  await checkIfEmailAndPasswordAreSet(email, password)
  const user = await findUserByEmail(email)
  if (!user) throw new Error('user not found')
  const result = await checkUserPassword(user, password)
  if (!result) throw new Error('incorrect password')
  await updateUserLastLoggedIn(user)
  const token = await generateSessionToken(user)
  return successResponse(user, token)
} catch (e) {
  // How to handle the error here?
}

我想保持正确的错误处理,即如果在checkUserPassword方法中抛出错误,我希望响应包含有关此的信息。我应该在catch方法中写什么?

例如,我可以将每条指令包装到它自己的try / catch块中:

try {

  let user, result

  try {
    await checkIfEmailAndPasswordAreSet(email, password)
  } catch (error) {
    throw new Error('This error was thrown in checkIfEmailAndPasswordAreSet')
  }

  try {
    user = await findUserByEmail(email)
  } catch (error) {
    throw new Error('This error was thrown in findUserByEmail')
  }

  if (!user) throw new Error('user not found')

  ...
} catch (error) {
  return errorResponse(error)
}

但是这段代码......可能不是回调地狱,但我会称它为try / catch hell。它使用原始旧式代码与回调相比至少需要2倍的行数。如何将其重写为更短并利用async / await?

1 个答案:

答案 0 :(得分:8)

try / catch没有easy handling of individual errors

我可能会编写一些辅助函数来简化错误抛出:

function error(code, msg) {
    return e => {
        e.status = code;
        e.details = msg;
        throw e;
    };
}
function ifEmpty(fn) {
    return o => o || fn(new Error("empty"));
}

然后使用标准的promise方法:

try {
    await checkIfEmailAndPasswordAreSet(email, password)
        .catch(error(403, 'validation error'));
    const user = await findUserByEmail(email)
        .then(ifEmpty(error(403, 'user not found')), error(500, 'db error', error));
    await checkUserPassword(user, password)
        .then(ifEmpty(error(403, 'incorrect password')), error(500, 'bcrypt error'));
    await updateUserLastLoggedIn(user)
        .catch(error(500, 'db error'));
    const token = generateSessionToken(user)
        .catch(error(500, 'jwt error'));
    return successResponse(user, token);
} catch(err) {
    errorResponse(err.status, err.details, err);
}