实现async/await

159 阅读2分钟

async 函数

async 函数就是 Generator 函数的语法糖。async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成awaitasync函数对 Generator 函数的改进,体现在以下四点。

  • 内置执行器。

Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。 调用了函数,它就会自动执行,输出最后结果。完全不像 Generator 函数,需要调用next方法,或者用co模块,才能真正执行,得到最后结果。

Thunk 函数是自动执行 Generator 函数的一种方法。编译器的“传名调用”实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做 Thunk 函数。 co模块是一个用于 Generator 函数的自动执行小工具。co 模块其实就是将两种自动执行器(Thunk 函数和 Promise 对象),包装成一个模块。使用 co 的前提条件是,Generator 函数的yield命令后面,只能是 Thunk 函数或 Promise 对象。如果数组或对象的成员,全部都是 Promise 对象,也可以使用 co。

  • 更好的语义。

asyncawait,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

  • 更广的适用性。

co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。

(4)返回值是 Promise。

async函数的返回值是 Promise 对象,比 Generator 函数的返回值是 Iterator 对象方便多了。可以用then方法指定下一步的操作。async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

实现原理

async 函数的实现原理,就是将 Generator 函数和自动执行器(基于Promise对象的自动执行,将异步操作包装成 Promise 对象,用then方法交回执行权。),包装在一个函数里。

function asyncToGenerator(generatorFunc) {
    return function() {
      const gen = generatorFunc.apply(this, arguments)
      return new Promise((resolve, reject) => {
        function step(key, arg) {
          let generatorResult
          try {
            generatorResult = gen[key](arg)
          } catch (error) {
            return reject(error)
          }
          const { value, done } = generatorResult
          if (done) {
            return resolve(value)
          } else {
            return Promise.resolve(value).then(val => step('next', val), err => step('throw', err))
          }
        }
        step("next")
      })
    }
}

参考资料