手写`async & await`

97 阅读2分钟

实现思路

  • 你需要提供暂停执行的能力
  • 你需要接收resolve的返回值

基于上述要求,可以想到生成器来实现此功能

生成器的简单使用

首先需要用 * 关键字定义生成器

当执行时,遇到yield关键字会停止执行,并返回值

function* generator() {
    console.log('start...')

    const a = yield 1
    console.log('next函数返回的字符串', a)
}

那么如何执行呢?

// 返回一个生成器对象
const g = generator()
/**
 * 第一次调用`next` 会开始迭代 遇到`yield`关键字停止 并返回一个对象
 * `value`代表值  done标记是否迭代完成
 */
const r1 = g.next()
console.log('第一次调用next', r1, '\n')

调用函数得到生成器,然后用next函数即可迭代

来看看结果

start...
第一次调用next { value: 1, done: false } 

他遇到yield关键字会停止执行,并返回值

那么我们怎么传递值呢

  • next函数也能传递参数,这个参数可以在生成器里接收

image.png

ok,生成器的简单使用就到这了,接下来实现类似async & await

这里调用的公开免费的接口

实现方式就是递归,如果你用while循环会阻塞js

判断Promise最好别用instanceof,因为babel降级打包后,你这判断就不行了

接下来大家想怎么封装都行

// 生成器模拟async
run()

function* generator() {
    const initVal = yield fetch('https://v.api.aa1.cn/api/yiyan/index.php'),
        // `initVal`的值 是`next`传递过来的
        res = yield initVal.text()
    console.log(initVal)
    console.log(res)
}

function run() {
    const g = generator()
    // 获取生成器的第一个`yield`值
    let res = g.next()
    _runTask()

    function _runTask() {
        if (res.done) return
        if (isPromise(res.value)) {
            res.value.then(data => {
                // `next`接受的参数将传递到`yield`的结果
                res = g.next(data)
                // `_runTask`不能统一放在最底下 会导致内存溢出 因为then是微任务
                _runTask()
            }).catch(err => {
                g.throw(err)
            })
        }
        else {
            res = g.next(res.value)
            _runTask()
        }
    }
}

function isPromise(target) {
    return typeof target === 'object' && typeof target?.then === 'function'
}

打印结果

image.png