利用生成器手写async await

1,108 阅读3分钟

阅读本文前, 你需要准备前置知识: ES6生成器 - generator

async awaitES7的异步解决方案,让我们的异步处理函数能使用同步代码的方式编写,其实内部是巧妙的结合了ES6中的Promise和生成器

一、生成器函数

  • 生成器函数就是普通函结构加上 *yield

生成器函数以下写法都是正确的,看例子:

// 靠着函数名
function *generator(){
	yiled 'cellphone'
}
// 靠着函数声明
function* generator(){
	yield 'cellphone'
}
// 两边都不靠
function * generator(){
	yield 'cellphone'
}
  • 生成器函数使用
// 声明两个Promise模拟异步
const getData = Promise.resolve(1)
const login = Promise.resolve(2)
// 实现的效果如果可以像下面代码这么写,
// 就相当于实现了async await
function *fn() {
    const a = yield getData;
    console.log(a); // 1
    const b = yield login;
    console.log(b); // 2
}
// 关键是 怎么处理生成器函数,
// 才能得到上面代码的结果呢,往下看
// 1.fn像普通函数那样执行,但并不会真正执行,
//仅仅是得到迭代器gen,gen.next()才能控制函数的执行
const gen = fn();

/*第一次调用next的时候,函数开始执行 遇到第一个yeild,
函数停在会执行到yield处,并且返回yield的结果,
yiled的结果一个对象 {value:getData, done: false} 
value就是yield的结果,done表达函数是否执行完毕。
这里的getaData就是返回的结果*/
const p1 = gen.next().value
p1.then(e => {
	// 第二次next(e + 100) e + 100会传回
    // 等待第一个yield的结果,这里就是a = e + 100
    const p2 = gen.next(e + 100).value
    p2.then(r => {
    	// 这里以此类推 b = r + 200
        const p3 = gen.next(r + 200)
        console.log(p3);
    })
})

二、async函数实现

  • 实现之前我们来看看要达到什么效果 也就是我们熟知的async await的写法,除了async* 代替,awaityield代替,其他简直就是一毛一样。
// ES7 async await写法
const getData = Promise.resolve(1)
const login = Promise.resolve(2)
function async fn() {
    const a = await getData;
    console.log(a);
    const b = await login;
    console.log(b);
    return 'someValue'
}
// 我们使用生成器写法 我们这样写是需要对结果进行一定的处理的 
// 这就是下一步的myAsync函数实现的核心逻辑
// 也就是原生async await实现的原理
const getData = Promise.resolve(1)
const login = Promise.resolve(2)
function* fn() {
    const a = yield getData;
    console.log(a); // 1
    const b = yield login;
    console.log(b); // 2
    return 'someValue'
}
/**
 * 逐行解释代码
 * @param {生成器函数} generator
 */
function myAsync(generator) {
    // 返回函数,以防需要传参
    return function () {
        // 执行生成器函数 获得迭代器 gen
        const gen = generator.apply(this, arguments)
         if (!gen || !gen.next) throw new Error('function myAsync 参数不是生成器函数')
        // Promise的作用用来处理函数的返回值
        return new Promise( resolve => {
            // 对出现的yield进行递归的函数
            function step(gen, result) {
                // 返回上一次的执行结果result 并获得下个yield
                const rv = gen.next(result)
                // 递归终止,使用resolve把最终return的结果返回
                if (rv.done) {
                    resolve(rv.value)
                    return
                }
                // 获取yiled的值
                const p = rv.value
                // 判断是是Promise 还是普通值进行递归
                p instanceof Promise ? p.then(r => {
                    step(gen, r)
                }) : step(gen, p)
            }
            step(gen)
        })
    }
}
 // 验证
 const result = myAsync(fn)

三、小结

上面的代码实现的是核心代码,很多情况没有考虑,也没有错误处理,但是能很好看出生成器是怎么工作的,以及巧妙的借用它实现asyncawait

PS:第一次码文章,有什么建议欢迎留言。