Skip to content

Latest commit

 

History

History
110 lines (85 loc) · 4.01 KB

实现 async、await.md

File metadata and controls

110 lines (85 loc) · 4.01 KB

实现 async/await

我们知道 async/awaitgenerator 函数的语法糖,它的作用是用同步的方式,执行异步操作。

本文将看看如何实现一个 async/await。假定你已经熟悉 async/awaitgenerator 函数。

首先,我们通过封装一个高阶函数 asyncToGenerator,该函数接收一个 generator 函数作为参数 generatorFn,并经过一系列处理,最终返回一个具有与 async 相同功能的新函数。

我们知道,async 最终返回的是一个 Promise 对象,那我们该如何返回一个 Promise 对象呢?

async function foo() {
  return 1
}

foo()
// Promise {<fulfilled>: 1}

熟悉 ES6 的你肯定立马想到答案,使用 Promise 构造函数包装一下:

function asyncToGenerator(generatorFn) {
  return function () {
    return new Promise((resolve, reject) => {})
  }
}

在返回的函数中,调用了 generator 函数 generatorFn,并将返回值赋给变量 gen。然后返回一个 Promise 对象,并在其中定义了一个内部函数 step

function asyncToGenerator(generatorFn) {
  return function () {
    const gen = generatorFn.apply(this, arguments) // gen 有可能传参

    return new Promise((resolve, reject) => {
      function step(key, arg) {}
    })
  }
}

当调用 step 函数时,会执行 generator 函数的下一步。如果 generator 函数的执行已经完成,则会触发 Promise 对象的 resolve 方法,并将 generator 函数的返回值作为参数传入。如果 generator 函数的执行尚未完成,则会返回一个新的 Promise 对象,并继续调用 step 函数执行 generator 函数的下一步。

function asyncToGenerator(generatorFn) {
  return function () {
    const gen = generatorFn.apply(this, arguments) // gen 有可能传参

    return new Promise((resolve, reject) => {
      function step(key, arg) {
        let ret

        // 可能存在返回 reject 状态的 Promise,使用 try...catch 捕获,并直接 reject
        try {
          ret = gen[key](arg)
        } catch (error) {
          return reject(error)
        }

        const { value, done } = ret

        // 如果 done 为 true,则表示 generator 函数的执行已经完成,返回 resolve(value)
        // value 是 generator 函数的返回值
        if (done) {
          return resolve(value)
        } else {
          // 如果 done 为 false,表示 generator 函数的执行尚未完成,
          // 返回一个新的 Promise 对象,并继续调用 step 函数执行 generator 函数的下一步。
          // 注意:value 有可能是成功或失败的,所以需要使用 Promise.resolve() 包装一下。
          return Promise.resolve(value).then(
            (val) => {
              step('next', val)
            },
            (err) => {
              step('throw', err)
            }
          )
        }
      }

      // 初次执行
      step('next')
    })
  }
}

这就是实现 async/await 的全部。

如果你使用 Babel 去转换一段 async/await 代码,得到的也是类似的实现。

在 generator 函数执行过程中,如果 generator 函数中有异步操作,可以返回一个 Promise 对象,然后在 generator 函数的下一步中使用 yield 关键字获取异步操作的结果。例如:

function* generatorFn() {
  const data = yield Promise.resolve(123)
  return data
}

当调用返回的函数时,会执行 generator 函数的第一步,然后返回一个 Promise 对象。你可以通过调用 then 方法或使用 await 关键字来处理 generator 函数的返回值。

asyncToGenerator(generatorFn)().then(console.log) // 123
await asyncToGenerator(generatorFn)()

需要注意,如果 generator 函数中的异步操作返回了一个失败的 Promise 对象,则会触发 Promise 对象的 reject 方法,并将错误信息传入。