js中使用生成器generator来实现async await

2,051 阅读2分钟

我们都知道async/await是基于Promise 的语法糖,他可以阻塞我们的异步操作来让我们更好的控制我们的异步任务,那么async await是怎么实现的呢,上一章节我们讲解过生成器,生成器每次执行next后就会停留在下一次yield上,这样就是不是很像我们await的阻塞,那么我们是不是就可以理解为我们的async、await其实是可以通过生成器来实现的,我们看下面这一段函数

const test=()=>new Promise((resolve,reject)=>
setTimeout(()=>{
    resolve('test')
},1000))

async function func(){
   const data= await test()
    console.log(data);
   const data= await test()
    console.log(data);
}

这一段函数就可以使用生成器来模拟实现,模拟成下面这一段。

function* func(){
   const data= yield test()
   console.log(data);
   const data= yield test()
   console.log(data); 
}

但是呢,生成器无法自动执行,一定要执行生成迭代器后手动调用next才能执行,我们可以手动执行一下上面这个方法

const it=func();
const p=it.next();//返回{value:Promise,done:false}
p.value.then(res=>{
    console.log(res);
    const p2=it.next();
    p2.value.then(res=>{
        console.log(res);
    })
})

是不是有一种回调地狱的感觉,如果我们要yield test()直接返回resolve的值,首先执行到yield test()时,值是还没有确定的,什么时候会确定呢,就是执行下一次next方法时,通过传参数next(val)的方式来确定值,什么意思呢,看下面这段代码

function* func(){
    const ans=yield test();
    console.log(ans);
    const ans2=yield test();
    console.log(ans2);
}

const it=func();
const p=it.next();//返回{value:Promise,done:false},这是ans并没有获取到resolve的值
p.value.then(res=>{
    console.log(res);
    const p2=it.next(res); //这一步时上面代码中的ans才会被确定值为res;
    p2.value.then(res=>{
        console.log(res);
    })
})

上面代码还是手动执行的,且我们最后一个yield是拿不到值的那么我们怎么修改才能拿到值呢,很简单,只要稍微改一点就行了,改为一个简单的递归即可。

function autoStart(generator){
        const gen=generator();//执行生成器函数
        function _next(val){
            const p=gen.next(val);
            if(p.done) return p.value;  //递归结束
            p.then(res=>{
                _next(res); //递归
            })
        }
        _next();
}
auto(func);

上面我们就基本实现了一个简单的async/await,然后我们在一步步优化,首先async函数是返回的Promise,其次还要进行异常处理,所以上面我们就要改成返回Promise并对其异常进行处理

function autoStart(generator){
    const gen=generator();
    function _next(val){
        return new Promise((resolve,reject)=>{
            try{
                var p=gen.next(val);
                if(p.done) resolve(p.value);
            }catch(err){
                reject(err);
            }
            //防止p.value为基本类型的情况
            Promise.resolve(p.value).then(res=>{
                _next(res);
            })
        })
    }
    _next();
}

最后我们使用这段代码来尝试一下

完整demo

const test =(data)=>new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve(data);
    })
})

function autoStart(generator){
    const gen=generator();
    function _next(val){
        return new Promise((resolve,reject)=>{
            try{
                var p=gen.next(val);
                if(p.done) resolve(p.value);
            }catch(err){
                reject(err);
            }
            //防止p.value为基本类型的情况
            Promise.resolve(p.value).then(res=>{
                _next(res);
            })
        })
    }
    _next();
}

function* generator(){
        const data=yield test(1);
        console.log(data);
        console.log(2);
        const data2=yield 3;
        console.log(data2)
        console.log(4);
}
autoStart(generator);
// 1
// 2
// 3
// 4
//完美输出 

参考文章
手写async await的最简实现(20行) 9k字 | Promise/async/Generator实现原理解析