likes
comments
collection
share

从源码解析await-to-js🌸🌸🌸

作者站长头像
站长
· 阅读数 37

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-to-js🌸🌸🌸

这就使得我们需要将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的不足以及使用场景。

参考:

www.npmjs.com/package/awa…

github.com/scopsy/awai…

blog.grossman.io/how-to-writ…

ES6 入门教程

async 函数 - JavaScript | MDN

从源码解析await-to-js🌸🌸🌸

转载自:https://juejin.cn/post/7203749658530070587
评论
请登录