实现一个简单的 async/await

239 阅读2分钟

我们都知道async/await 是生成器函数Generator 的语法糖,那么我们先来探究一下JavaScript中的生成器函数吧。

Generator即生成器函数是ES6提供的一种一部解决方案,Generator是一个内部包含了多种状态的状态机。下面是一个生成器函数:

function* myGenerator () {
    yield 'hello'
    yield 'world'
    return 'ending'
}

const fn = myGenerator()

console.log(fn.next())
console.log(fn.next())
console.log(fn.next())

他的打印结果是这样的:

调用生成器函数的next方法,返回的是一个包含值和状态的遍历器对象,可以看到当对象的done是false时,说明遇到了return语句。

当一个生成器函数如下时

function* sum() {
	yield 3 + 4
}

当没有执行到对应的next语句时,yield后面的计算时不会执行的,也就是所谓的惰性执行。

那么generator函数与async函数的关联是什么呢,通过学习我发现其实async/await其实就是generator加上一个自动执行函数。因为generator是无法自动执行的,必须通过next方法手动执行。 所以只要我们将generator外包裹一个自动执行函数,并返回一个promise,就可以模拟async/await的效果,下面我来实现一下:

const { resolve } = require("path");

/*
    测试函数,返回一个promise
*/
const getData = function(n, time) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(n)
        }, time);
    })
}

/*
生成器函数,通过autoTick函数让其自执行
*/
function* myGenerator () {
    const n1 = yield getData(10,2000);
    console.log(n1)
    const n2 = yield getData(20,2000);
    console.log(n2)
    return 
}

/*
自动执行函数,可将其接收的生成器函数自动执行
*/
function autoTick(generatorFunc) {
    return function() {
        const gen = generatorFunc.apply(this, arguments) 
        return new Promise((resolve,reject) => {
            let res
            function step (key, args) {
                try{
                    res = gen[key](args)
                } catch(e) {
                    reject(e)
                }    
            const  { value, done } = res
            if(done) {
                return resolve(value)
            } else {
                return Promise.resolve(value).then(value => {
                    step('next', value)
                })
            }
        }
        step('next')
    }
        )
    }
}
autoTick(myGenerator)()


可以看到打印结果是如下,并且每个数字的打印间隔都是2000ms:

其中的myGenerator 方法其实就等价于如下写法:

function* myGenerator () {
    const n1 = yield getData(10,2000);
    console.log(n1)
    const n2 = yield getData(20,2000);
    console.log(n2)
    return 
}
等价于>>>>>
async myGenerator() {
    const n1 = await getData(10,2000);
    console.log(n1)
    const n2 = await getData(20,2000);
    console.log(n2)
    return 
}
//他们唯一的区别就是写法和能否自执行了

通过这次探究我加深了对生成器函数,迭代器,async/await的理解,而不仅仅停留在会用的层面。