本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
思考一个问题:怎样捕获async函数中Promise的错误?
在async函数中,常见捕获Promise错误方式有两种:
- Promise.prototype.catch()
async function demo() {
Promise.reject('failed')
.catch(console.log); // failed
}
demo();
本质上是Promise的链式调用。仍然是传统异步代码的书写方式。
- try catch,一般用于async函数的内部
async function demo() {
try {
await Promise.reject('failed');
} catch (e) {
console.log(e); // failed
}
}
demo();
try catch的书写方式更近似同步代码,但是每个报错的地方都要try catch, 不够优雅。
有没有办法既实现近似同步代码的书写体验又减少重复的try catch,优雅地捕获错误呢?await-to-js提供了一种思路。
用法举例
import to from 'await-to-js';
const successTask = () => {
return Promise.resolve('success');
};
const failedTask = () => {
return Promise.reject(new Error('failed'));
};
async function demo() {
// to方法第一个参数接收Promise,await后的结果是一个数组。
const [err1, data1] = await to(successTask());
// 返回值数组的第一个元素保存Promise抛出的异常
console.log(err1); // null
// 返回值数组的第二个元素保存Promise的返回值
console.log(data1); // success
const [err2, data2] = await to(failedTask());
console.log(err2!.message); // failed
console.log(data2); // undefined
// to第二个参数是用户自定义的错误信息,可以补充或覆盖原来的错误信息
const [err3] = await to(failedTask(), { message: 'Mock Error Info' });
console.log(err3!.message); // Mock Error Info
}
demo();
源码解析
await-to-js 源码非常简单,只有一个函数to。
/**
* @param { Promise } promise
* @param { Object= } errorExt - Additional Information you can pass to the err object
* @return { Promise }
*/
export function to<T, U = Error> (
promise: Promise<T>,
errorExt?: object
): Promise<[U, undefined] | [null, T]> {
return promise
.then<[null, T]>((data: T) => [null, data])
.catch<[U, undefined]>((err: U) => {
if (errorExt) {
const parsedError = Object.assign({}, err, errorExt);
return [parsedError, undefined];
}
return [err, undefined];
});
}
export default to;
1)to 支持传两个参数,第一个参数接收待执行的Promise,第二个参数接收用户自定义的错误信息。
2)通过await得到一个长度为2的数组。数组的第一个元素为Promise抛出的错误,第二个元素为Promise的返回值。
3)to 内部通过Promise.prototype.catch方法捕获错误。
基于to的封装和抽象,我们可以用一行代码得到Promise执行结果并优雅地捕获Promise的错误。
async function demo() {
const [err, data] = await to(anyPromiseTask());
}
demo();
总结
await-to-js 基于Promise.prototype.catch优雅地封装了Promise可能抛出的异常,使我们可以用更加近似同步代码的书写方式书写Promise异步代码。