假设我们有一个在用户登录(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?
答案 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);
}