sggmico / fe-happy-interview

面试不迷茫
Apache License 2.0
5 stars 0 forks source link

【手写实现】:Async/Await实现原理 #10

Open sggmico opened 3 years ago

sggmico commented 3 years ago

【手写实现】:Async/Await实现原理

Async/Await 是在 2017年的ES8中新加入的ES语法,作为一种比较优雅的异步编程解决方案,可以在不阻塞主线程的的情况下,使用同步代码实现异步编程的能力。async函数调用时会返回一个 Promise

实际上,Async/Await 是基于Promisegenerator函数 的语法糖。

先来看下 Async/Await 的简单应用:

/**
 * async/await语法
 *
 * @returns
 */
async function hello() {
    await 'a'
    await 'b'
    return 'c'
}

hello().then(data => console.log(data)) // => 'c'

上面代码中,hello为一个async函数,调用时返回promise, 最后已决成功状态会打印 字符串 'c'

下面我们基于 Promisegenerator 函数 来实现 Async/Await 语法的效果。

先使用 generator语法声明一个函数:

/**
 * generator函数
 *
 * @returns
 */
function* _hello() {
    yield 'a'
    yield 'b'
    return 'c'
}

var gen = _hello() // 仅返回一个迭代器

console.log(gen.next())  // { value: 'a', done: false }
console.log(gen.next())  // { value: 'b', done: false }
console.log(gen.next())  // { value: 'c', done: true }
console.log(gen.next())  // { value: undefined, done: true }

上面的 _hello函数调用时,返回一个迭代器,迭代器提供了 next() API, 通过手动依次调用迭代器的 next(),可以依次得到 yieldreturn 的结果,结果为一个对象,包含 两个字段: valuedone

接下来,我们将通过一个函数对hello函数(generator函数)进行处理。代码如下:

// 基于 generator函数实现 async/await (前者的语法糖)
function _asyncToGenerator(fn) {
    return function () {
        //gen 为 generator迭代器iterator
        var gen = fn.apply(this, arguments)
        return new Promise(function (resolve, reject) {
            function _next(key, arg) {
                var genRet
                try {
                    genRet = gen[key](arg)
                } catch (error) {
                    reject(error)
                    return
                }
                const { value, done } = genRet
                if (done) {
                    // 所有next执行完,获取到异步函数最后结果
                    resolve(value)
                } else {
                    // 自动执行后续next
                    Promise.resolve(value)
                        .then(data => _next('next', data))
                        .catch(err => _next('throw', err))
                }
            }
            _next('next')
        })
    }
}

var newHello = _asyncToGenerator(_hello)

newHello().then(data => console.log(data))

上面的 _asyncToGenerator 函数,主要做两件事情:

  1. 返回一个新函数
  2. 为了实现 Async/Await 的语法特性,新函数需要处理:
    1. 调用generator函数生成一个迭代器
    2. 实例化一个Promise,并在回调函数内,递归调用迭代器的 next() 接口,直到结束,即: done: true
    3. 返回一个 Promise

最后,_hello函数经过_asyncToGenerator 函数处理后,和本文开始的 hello函数(Async/Await语法)实现相同的效果。

参考文章

  1. sl1673495 - async的执行原理
  2. ES6 系列之 Babel 将 Generator 编译成了什么样子