从源码解析await-to-js🌸🌸🌸
async函数是一种新的异步编程解决方案。这种方案是在ES2017中开始引入。async 函数是AsyncFunction构造函数的实例。async函数相较于Promise、Generator,让我们可以用一种具有更好语义、更广的适用性的方式处理异步操作。 但是async的错误会阻塞后续代码的执行。从下图可知:
f函数中await后异步函数报错会导致第5行的console.log(1)不执行。
async function f() {
await new Promise(function (resolve, reject) {
throw new Error('出错了');
});
console.log(1)
}
f()
.then(v => console.log(v))
.catch(e => console.log(e))
这就使得我们需要将await放到try...catch中。
async function f() {
try {
await new Promise(function (resolve, reject) {
throw new Error('出错了');
});
} catch(e) {
}
return await('hello world');
}
如果有多个await,有两种方案可以捕获异常:
- 方案一是将其分别放入try...catch
- 方案二是整体放入try...catch。方案一虽然可以较好的捕获调用链的每个环节的错误,但是写法较为繁杂。方案二虽然写法简洁,但是不能捕获错误环节。
// 方案一
function async asyncTask(cb) {
try {
const asyncFuncARes = await asyncFuncA()
} catch(error) {
return new Error(error)
}
try {
const asyncFuncBRes = await asyncFuncB(asyncFuncARes)
} catch(error) {
return new Error(error)
}
try {
const asyncFuncCRes = await asyncFuncC(asyncFuncBRes)
} catch(error) {
return new Error(error)
}
}
// 方案二
function async asyncTask(cb) {
try {
const asyncFuncARes = await asyncFuncA()
const asyncFuncBRes = await asyncFuncB(asyncFuncARes)
const asyncFuncCRes = await asyncFuncC(asyncFuncBRes)
} catch(error){
return new Error(error)
}
}
await-to-js作为一种async/await错误处理的库收到了越来越多开发者的亲睐,npm上的周下载量达到了755402,GitHub上的star数达到了2.5k。
await-to-js的使用
await-to-js的使用非常简单,主要是利用其暴露的to函数对await后面的异步函数进行包裹,返回结果为异常和成功的数组。
上述代码可以写成:
import to from 'await-to-js'
function async asyncTask(cb) {
const [asyncFuncAErr, asyncFuncARes] = await to(asyncFuncA())
const [asyncFuncBErr, asyncFuncBRes] = await to(asyncFuncB(asyncFuncARes))
const [asyncFuncCErr, asyncFuncCRes] = await to(asyncFuncC(asyncFuncBRes))
}
awai-to-js的源码分析
await-to-js的作用很大,但核心源码非常简单。await-to-js的核心源码在src/await-to-js.ts这个文件中,具体源码如下:
/**
* @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;
从上述源码可知,to函数的入参为Promise对象promise和可选错误信息对象,返回则是加入Promise处理逻辑的Promise对象。
返回的Promise的then中返回[null,data] ,null表示没有发错错误(异常),data为实际的数据,而catch中判断是否提供了额外的错误对象,如果有则合并Promise返回的异常和错误对象,然后返回[parsedError, undefined]表示发生了错误没有数据。如果没有提供额外的错误对象,则新promise返回的异常和undefined,即[err, undefined]。最后导出一个对象以及默认导出,为了支持不同的导入方式。
通过上述源码分析可知,to函数本质上是将原Promise对象转换成一个肯定会成功返回的Promise对象,这也决定了await-to-js并非解决async函数异常捕获的银弹。
首先,增加了开发者的理解成本,开发者需要查阅to函数的定义才能明确知道to函数返回的Promise是永远会成功。其次,to函数更多应用于多个await的场景下,而这种场景只是async函数众多应用场景中的一部分。
总结
本文从async函数的异常捕获问题出发,先介绍了await-to-js库的使用,接着对其源码进行了分析,随后从源码角度,指出了await-to-js的不足以及使用场景。
参考:
转载自:https://juejin.cn/post/7203749658530070587