谈谈 generator 与 async

201 阅读2分钟

generator

generator 的中文是生成器,它可以返回多次,不像普通函数,只能使用一次 return 来返回,yield 与遍历器对象的 next 方法配合可以进行多次返回

function* gen(x){
    const y=yield x+1
    const z=yield y*2
    return x+y+z
}
let g=gen(3)
g.next()
g.next(4)
g.next(5)

//
{value:12,done:true}

next 不仅会启动 yield,也可以传参,不过是向上一个 yield 传参,换句话说,第一次调用 next 无需传参,或者说传参无效,只是用来启动 generator

分析一下过程:

调用 gen 函数,得到遍历器对象,并且参数为 3,这时 x 的值为 3

第一次调用 next,没有传参,x 的值为 3,返回的结果为 {value:4,done:false}

第二次调用 next,传参为 5,x 的值不变,为第一个 yield 传参,所以 y 的值变为 4,即 z 的值为 8,所以结果为 {value:8,done:false}

第三次调用 next,为第二个 yield 传参 5,所以 z 的值为 5,此时,x 为 3,y 为 4,z 为 5,结果为 {value:12,done:true}

async

async 是 promise 的语法糖之一,一般配合 await 使用,用来等待 promise,一种简洁的写法

async function async1(){
    return 2
}

等价于

function async1(){
    return Promise.resolve(2)
}

那么如果在 async 函数中返回 promise 会怎样呢

async function async1(){
    return new Promise((resolve,reject)=>{
        resolve(2)
    })
}

这样的话由返回的 promise 决定状态,也就是

function async1(){
    return new Promise((resolve,reject)=>{
        new Promise((resolve2,reject2)=>{
            resolve2()
        }).then(()=>{
            resolve()
        })
    })
}

前置知识已经够用了,让我们用 generator 的原理来实现简单的 async

对照着例子来写会更好理解一些,先做好例子

function* genF(){
    console.log(1)
    const t=yield p1(1)
    console.log(t)
    console.log(3)
    const t2=yield 4
    console.log(t2)
    return new Promise((resolve,reject)=>{
        resolve(5)
    })
}

简而言之,我们要做一个模仿 async/await 的函数,让其自动执行,而且也会产生阻塞代码的效果,generator 可以帮助我们

function async(genF,...initialValue){
    return new Promise((resolve,reject)=>{
        let gen=genF.apply(null,initialValue)
        function step(value){
           let result=gen.next(value)
           if(result.done){
               resolve(result.value)
               return;
           }
           Promise.resolve(result.value).then(data=>{
               step(data)
           },e=>{
               reject(e)
           })
        }
    })
}

我们必须手动开启 step 函数,step 函数会不断地取 next 返回的对象的 value,如果是 promise 的话,等待其 resolve 并传值给下次 step 函数,如果为非 promise 的话,将该值封装为 fulfilled 的 promise

在 result.done 为真时,说明已经走到了函数的返回值,我们可以直接 resolve 结果,如果是 promise 的话,async 的返回值的状态依旧由 result.value 来决定

来测试一下

function async(genF,...initialValue){
    return new Promise((resolve,reject)=>{
        let gen=genF.apply(null,initialValue)
        step()
        function step(value){
           let result=gen.next(value)
           if(result.done){
               resolve(result.value)
               return;
           }
           Promise.resolve(result.value).then(data=>{
               step(data)
           },e=>{
               reject(e)
           })
        }
    })
}
const p1=(t)=>new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve(t*1000)
    },t*1000)
})
function* genF(){
    console.log(1)
    const t=yield p1(1)
    console.log(t)
    console.log(3)
    const t2=yield 4
    console.log(t2)
    const t3=yield p1(2)
    console.log(t3)
    return new Promise((resolve,reject)=>{
        resolve(5)
    })
}
async(genF).then(data=>{
    console.log('end',data)
})

核心就是 generator 的执行机制和 promise 的状态与值的传递,此次总结也让我更加了解 promise