使用 Async/Await 正确的 Try...Catch 语法
Correct Try...Catch Syntax Using Async/Await
我喜欢 Typescript 等中可用的新 Async/Await
功能的平坦度。但是,我不确定我是否喜欢我必须声明变量的事实 await
在 try...catch
块的外面,以便以后使用它。像这样:
let createdUser
try {
createdUser = await this.User.create(userInfo)
} catch (error) {
console.error(error)
}
console.log(createdUser)
// business
// logic
// goes
// here
如果我错了请纠正我,但最好的做法是 而不是 将多行业务逻辑放在 try
正文中,所以我我只剩下在块外声明 createdUser
,在块中分配它,然后在之后使用它的替代方法。
这种情况下的最佳做法是什么?
It seems to be best practice not to place multiple lines of business logic in the try body
实际上我会说是。您通常希望 catch
所有 异常都不会使用以下值:
try {
const createdUser = await this.User.create(userInfo);
console.log(createdUser)
// business logic goes here
} catch (error) {
console.error(error) // from creation or business logic
}
如果你只想从 promise 中捕获和处理错误,你有以下三种选择:
在外面声明变量,根据有无异常进行分支。这可以采用各种形式,例如
- 为
catch
块中的变量分配默认值
return
提早或重新 throw
来自 catch
块的异常
- 设置一个标志是否
catch
块捕获异常,并在if
条件下测试它
- 测试已赋值的变量值
let createdUser; // or use `var` inside the block
try {
createdUser = await this.User.create(userInfo);
} catch (error) {
console.error(error) // from creation
}
if (createdUser) { // user was successfully created
console.log(createdUser)
// business logic goes here
}
测试捕获的异常的类型,并据此处理或重新抛出它。
try {
const createdUser = await this.User.create(userInfo);
// user was successfully created
console.log(createdUser)
// business logic goes here
} catch (error) {
if (error instanceof CreationError) {
console.error(error) // from creation
} else {
throw error;
}
}
不幸的是,标准 JavaScript(仍然)没有 conditional exceptions.
的语法支持
如果您的方法没有 return promises 因足够具体的错误而被拒绝,您可以通过在 .catch()
处理程序中重新抛出更合适的内容来自己做到这一点:
try {
const createdUser = await this.User.create(userInfo).catch(err => {
throw new CreationError(err.message, {code: "USER_CREATE"});
});
…
} …
另请参阅 Handling multiple catches in promise chain 了解此 async
/await
之前的版本。
使用then
with two callbacks代替try
/catch
。这确实是最不丑陋的方式,我个人的建议也是因为它的简单性和正确性,不依赖于标记错误或结果值的外观来区分承诺的履行和拒绝:
await this.User.create(userInfo).then(createdUser => {
// user was successfully created
console.log(createdUser)
// business logic goes here
}, error => {
console.error(error) // from creation
});
当然它有引入回调函数的缺点,这意味着你不能那么容易地 break
/continue
循环或从外部函数做早期的 return
s。
另一种更简单的方法是将 .catch 附加到 promise 函数。例如:
const createdUser = await this.User.create(userInfo).catch( error => {
// handle error
})
@Bergi 回答很好,但我认为这不是最好的方法,因为你必须回到旧的 then() 方法,所以我认为更好的方法是在异步函数中捕获错误
async function someAsyncFunction(){
const createdUser = await this.User.create(userInfo);
console.log(createdUser)
}
someAsyncFunction().catch(console.log);
- 但是如果我们在同一个函数中有很多
await
并且需要捕获每个错误怎么办?
您可以声明 to()
函数
function to(promise) {
return promise.then(data => {
return [null, data];
})
.catch(err => [err]);
}
然后
async function someAsyncFunction(){
let err, createdUser, anotherUser;
[err, createdUser] = await to(this.User.create(userInfo));
if (err) console.log(`Error is ${err}`);
else console.log(`createdUser is ${createdUser}`);
[err, anotherUser] = await to(this.User.create(anotherUserInfo));
if (err) console.log(`Error is ${err}`);
else console.log(`anotherUser is ${anotherUser}`);
}
someAsyncFunction();
When reading this its: "Wait to this.User.create".
最后您可以创建模块 "to.js" 或者简单地使用 await-to-js 模块。
您可以在 this post
中获得有关 to
函数的更多信息
我通常使用 Promise 的 catch()
函数来 return 一个失败时 error
属性 的对象。
例如,在你的情况下我会这样做:
const createdUser = await this.User.create(userInfo)
.catch(error => { error }); // <--- the added catch
if (Object(createdUser).error) {
console.error(error)
}
如果您不想继续添加 catch()
调用,您可以向函数的原型添加一个辅助函数:
Function.prototype.withCatcher = function withCatcher() {
const result = this.apply(this, arguments);
if (!Object(result).catch) {
throw `${this.name}() must return a Promise when using withCatcher()`;
}
return result.catch(error => ({ error }));
};
现在您将能够做到:
const createdUser = await this.User.create.withCatcher(userInfo);
if (Object(createdUser).error) {
console.error(createdUser.error);
}
编辑 03/2020
您还可以向 Promise
对象添加默认 "catch to an error object" 函数,如下所示:
Promise.prototype.catchToObj = function catchToObj() {
return this.catch(error => ({ error }));
};
然后使用如下:
const createdUser = await this.User.create(userInfo).catchToObj();
if (createdUser && createdUser.error) {
console.error(createdUser.error);
}
更简洁的代码
async/await 使用 Promise 捕获处理程序进行错误处理。
据我所知,这是一个长期存在的问题,困扰着许多程序员和他们的代码。
ES6 Promise 的 catch handler 提供了一个合适的解决方案:
this.User.create(userInfo).then(createdUser => {
console.log(createdUser)
// business
// logic
// goes
// here
}).catch(err => {
//handle the error
})
但这看起来像是完全删除了 async/await。不对。
想法是使用 Promise 样式并捕获顶级调用者。否则继续使用async/await.
例如,除了创建用户(this.User.create
),我们还可以推送通知(this.pushNotification
)和发送电子邮件(this.sendEmail
)。都是异步操作。不需要捕获处理程序。就 async/await.
this.User.create
this.User.create = async(userInfo) => {
// collect some fb data and do some background check in parallel
const facebookDetails = await retrieveFacebookAsync(userInfo.email)
const backgroundCheck = await backgroundCheckAsync(userInfo.passportID)
if (backgroundCheck.pass !== true) throw Error('Background check failed')
// now we can insert everything
const createdUser = await Database.insert({ ...userInfo, ...facebookDetails })
return createdUser
}
this.pushNotifcation 和 this.sendEmail
this.pushNotification = async(userInfo) => {
const pushed = await PushNotificationProvider.send(userInfo)
return pushed
})
this.sendEmail = async(userInfo) => {
const sent = await mail({ to: userInfo.email, message: 'Welcome' })
return sent
})
当所有异步操作组合在一起时,我们可以使用附有#catch 处理程序的 Promise 样式:
this.User.create(userInfo).then(createdUser => {
console.log(createdUser)
// business logic here
return Promise.all([
this.pushNotification(userInfo),
this.sendEmail(userInfo)
])
}).catch(err => {
// handle err
})
如果我们用 try/catch 来做到这一点,就必须将所有内容都包装在 try/catch 中(不可取),或者设置许多 try/catches:
var createdUser
try {
createdUser = await this.User.create(userInfo)
} catch (err) {
//handle err
}
console.log(createdUser)
// business logic here
if (createdUser) {
try {
await this.pushNotification(userInfo)
await this.sendEmail(userInfo)
} catch (err) {
// handle err
}
}
我喜欢 Typescript 等中可用的新 Async/Await
功能的平坦度。但是,我不确定我是否喜欢我必须声明变量的事实 await
在 try...catch
块的外面,以便以后使用它。像这样:
let createdUser
try {
createdUser = await this.User.create(userInfo)
} catch (error) {
console.error(error)
}
console.log(createdUser)
// business
// logic
// goes
// here
如果我错了请纠正我,但最好的做法是 而不是 将多行业务逻辑放在 try
正文中,所以我我只剩下在块外声明 createdUser
,在块中分配它,然后在之后使用它的替代方法。
这种情况下的最佳做法是什么?
It seems to be best practice not to place multiple lines of business logic in the try body
实际上我会说是。您通常希望 catch
所有 异常都不会使用以下值:
try {
const createdUser = await this.User.create(userInfo);
console.log(createdUser)
// business logic goes here
} catch (error) {
console.error(error) // from creation or business logic
}
如果你只想从 promise 中捕获和处理错误,你有以下三种选择:
在外面声明变量,根据有无异常进行分支。这可以采用各种形式,例如
- 为
catch
块中的变量分配默认值 return
提早或重新throw
来自catch
块的异常- 设置一个标志是否
catch
块捕获异常,并在if
条件下测试它 - 测试已赋值的变量值
let createdUser; // or use `var` inside the block try { createdUser = await this.User.create(userInfo); } catch (error) { console.error(error) // from creation } if (createdUser) { // user was successfully created console.log(createdUser) // business logic goes here }
- 为
测试捕获的异常的类型,并据此处理或重新抛出它。
try { const createdUser = await this.User.create(userInfo); // user was successfully created console.log(createdUser) // business logic goes here } catch (error) { if (error instanceof CreationError) { console.error(error) // from creation } else { throw error; } }
不幸的是,标准 JavaScript(仍然)没有 conditional exceptions.
的语法支持如果您的方法没有 return promises 因足够具体的错误而被拒绝,您可以通过在
.catch()
处理程序中重新抛出更合适的内容来自己做到这一点:try { const createdUser = await this.User.create(userInfo).catch(err => { throw new CreationError(err.message, {code: "USER_CREATE"}); }); … } …
另请参阅 Handling multiple catches in promise chain 了解此
async
/await
之前的版本。使用
then
with two callbacks代替try
/catch
。这确实是最不丑陋的方式,我个人的建议也是因为它的简单性和正确性,不依赖于标记错误或结果值的外观来区分承诺的履行和拒绝:await this.User.create(userInfo).then(createdUser => { // user was successfully created console.log(createdUser) // business logic goes here }, error => { console.error(error) // from creation });
当然它有引入回调函数的缺点,这意味着你不能那么容易地
break
/continue
循环或从外部函数做早期的return
s。
另一种更简单的方法是将 .catch 附加到 promise 函数。例如:
const createdUser = await this.User.create(userInfo).catch( error => {
// handle error
})
@Bergi 回答很好,但我认为这不是最好的方法,因为你必须回到旧的 then() 方法,所以我认为更好的方法是在异步函数中捕获错误
async function someAsyncFunction(){
const createdUser = await this.User.create(userInfo);
console.log(createdUser)
}
someAsyncFunction().catch(console.log);
- 但是如果我们在同一个函数中有很多
await
并且需要捕获每个错误怎么办?
您可以声明 to()
函数
function to(promise) {
return promise.then(data => {
return [null, data];
})
.catch(err => [err]);
}
然后
async function someAsyncFunction(){
let err, createdUser, anotherUser;
[err, createdUser] = await to(this.User.create(userInfo));
if (err) console.log(`Error is ${err}`);
else console.log(`createdUser is ${createdUser}`);
[err, anotherUser] = await to(this.User.create(anotherUserInfo));
if (err) console.log(`Error is ${err}`);
else console.log(`anotherUser is ${anotherUser}`);
}
someAsyncFunction();
When reading this its: "Wait to this.User.create".
最后您可以创建模块 "to.js" 或者简单地使用 await-to-js 模块。
您可以在 this post
中获得有关to
函数的更多信息
我通常使用 Promise 的 catch()
函数来 return 一个失败时 error
属性 的对象。
例如,在你的情况下我会这样做:
const createdUser = await this.User.create(userInfo)
.catch(error => { error }); // <--- the added catch
if (Object(createdUser).error) {
console.error(error)
}
如果您不想继续添加 catch()
调用,您可以向函数的原型添加一个辅助函数:
Function.prototype.withCatcher = function withCatcher() {
const result = this.apply(this, arguments);
if (!Object(result).catch) {
throw `${this.name}() must return a Promise when using withCatcher()`;
}
return result.catch(error => ({ error }));
};
现在您将能够做到:
const createdUser = await this.User.create.withCatcher(userInfo);
if (Object(createdUser).error) {
console.error(createdUser.error);
}
编辑 03/2020
您还可以向 Promise
对象添加默认 "catch to an error object" 函数,如下所示:
Promise.prototype.catchToObj = function catchToObj() {
return this.catch(error => ({ error }));
};
然后使用如下:
const createdUser = await this.User.create(userInfo).catchToObj();
if (createdUser && createdUser.error) {
console.error(createdUser.error);
}
更简洁的代码
async/await 使用 Promise 捕获处理程序进行错误处理。
据我所知,这是一个长期存在的问题,困扰着许多程序员和他们的代码。
ES6 Promise 的 catch handler 提供了一个合适的解决方案:
this.User.create(userInfo).then(createdUser => {
console.log(createdUser)
// business
// logic
// goes
// here
}).catch(err => {
//handle the error
})
但这看起来像是完全删除了 async/await。不对。
想法是使用 Promise 样式并捕获顶级调用者。否则继续使用async/await.
例如,除了创建用户(this.User.create
),我们还可以推送通知(this.pushNotification
)和发送电子邮件(this.sendEmail
)。都是异步操作。不需要捕获处理程序。就 async/await.
this.User.create
this.User.create = async(userInfo) => {
// collect some fb data and do some background check in parallel
const facebookDetails = await retrieveFacebookAsync(userInfo.email)
const backgroundCheck = await backgroundCheckAsync(userInfo.passportID)
if (backgroundCheck.pass !== true) throw Error('Background check failed')
// now we can insert everything
const createdUser = await Database.insert({ ...userInfo, ...facebookDetails })
return createdUser
}
this.pushNotifcation 和 this.sendEmail
this.pushNotification = async(userInfo) => {
const pushed = await PushNotificationProvider.send(userInfo)
return pushed
})
this.sendEmail = async(userInfo) => {
const sent = await mail({ to: userInfo.email, message: 'Welcome' })
return sent
})
当所有异步操作组合在一起时,我们可以使用附有#catch 处理程序的 Promise 样式:
this.User.create(userInfo).then(createdUser => {
console.log(createdUser)
// business logic here
return Promise.all([
this.pushNotification(userInfo),
this.sendEmail(userInfo)
])
}).catch(err => {
// handle err
})
如果我们用 try/catch 来做到这一点,就必须将所有内容都包装在 try/catch 中(不可取),或者设置许多 try/catches:
var createdUser
try {
createdUser = await this.User.create(userInfo)
} catch (err) {
//handle err
}
console.log(createdUser)
// business logic here
if (createdUser) {
try {
await this.pushNotification(userInfo)
await this.sendEmail(userInfo)
} catch (err) {
// handle err
}
}