2023.29 生成器函数以及使用生成器实现async/await

109 阅读3分钟

大家好,我是wo不是黄蓉,今年学习目标从源码共读开始,希望能跟着若川大佬学习源码的思路学到更多的东西。

什么是生成器

生成器:是一种函数控制和使用的方案,可以让我们更加灵活的控制函数什么时候继续指向性、暂停执行等

如果已经执行完毕了,后面每次调用next的返回都是{ value: undefined, done: true }

有什么好处?

可以控制代码的执行,普通函数会将代码都执行一遍

表现形式:

  • 函数后面加个*

  • 代码执行可以被yield控制

  • 生成器函数默认在执行时,返回一个生成器对象

    • 要想执行函数内部的代码,需要生成器对象,调用他的Next操作
    • 当遇到yield时,会中断执行

举个例子:


function* helloWorldGenerator() {
    yield 'hello'
    yield 'world'
    return 'ending'
}
​
var hw = helloWorldGenerator()
console.log(hw) //返回一个生成器
console.log(hw.next())
//打印结果
Object [Generator] {}
{ value: 'hello', done: false }

next的时候可以传参

function* helloWorldGenerator() {
    console.log('111')
    const foo = yield 'hello'
    console.log(foo)
    const bar = yield 'world'
    console.log(bar)
    return 'ending'
}
​
var hw = helloWorldGenerator()
console.log(hw)
console.log(hw.next('i want wo say'))
console.log(hw.next('i want wo say1'))
console.log(hw.next('i want wo say2'))
console.log(hw.next('i want wo say3'))
//打印结果:
Object [Generator] {}
111
{ value: 'hello', done: false }
i want wo say1
{ value: 'world', done: false }
i want wo say2
{ value: 'ending', done: true }
{ value: undefined, done: true }

为什么第一次传参的next不起作用呢?

因为第一次调用next执行的代码是第一个yield之前的代码

参数接收是通过在yield之前获取的,如果想要第一次的时候传参,需要在调用helloWorldGenerator里面传参,在函数定义的地方接收参数

function* helloWorldGenerator(name) {
    console.log(name)
    console.log('111')
    const foo = yield 'hello'
    console.log(foo)
    const bar = yield 'world'
    console.log(bar)
    return 'ending'
}
​
var hw = helloWorldGenerator('i want wo say')
console.log(hw)
console.log(hw.next())
//打印结果:
Object [Generator] {}
i want wo say
111
{ value: 'hello', done: false }

生成器函数提前结束

使用throw或者return


function* getInfos() {
    const users = yield getUsers()
    console.log('users', users)
    throw Error('中断执行')
    // return
    const menus = yield getMenus()
    console.log('menus', menus)
}
​
const iterator = getInfos()
​
const result = iterator.next()
result.value
    .then((res) => {
        if (res.status) {
            console.log(res)
        }
    })
    .catch((err) => {
        console.log(err)
    })
const menus = iterator.next()
menus.value
    .then((resMenu) => {
        console.log(resMenu)
    })
    .catch((err) => {
        console.log(err)
    })

中断执行后,异步代码不会被执行到,在catch中捕获不到异常

1690954821582.png

生成器函数解决异步问题,解决回调地狱问题

实现异步:

function getUsers() {
    return new Promise((resolve) => {
        resolve({
            status: true,
            list: [
                { name: 'hp', age: 18 },
                { name: 'zhh', age: 18 }
            ]
        })
    })
}
​
function getMenus() {
    return new Promise((resolve) => {
        resolve({
            status: true,
            list: [
                { name: '一级菜单-1', pid: 0 },
                { name: '一级菜单-2', pid: 0 }
            ]
        })
    })
}
​
function* getInfos() {
    const users = yield getUsers()
    console.log('users', users)
    // throw Error('中断执行')
    // return
    const menus = yield getMenus()
    console.log('menus', menus)
}
​
​
const iterator = getInfos()
​
const result = iterator.next()
result.value.then((res) => {
    if (res.status) {
        console.log(res)
    }
})
const menus = iterator.next()
menus.value.then((resMenu) => {
    console.log(resMenu)
})

打印结果:

1690953374161.png

现在的代码可以实现按照同步编码的方式实现异步编程,但是每次都需要手动调用Netx如果上下有依赖关系,类似这样,是不是又有点回调地狱的意思


const iterator = getInfos()
​
const result = iterator.next()
result.value.then((res) => {
    if (res.status) {
        console.log(res)
        const menus = iterator.next()
        menus.value.then((resMenu) => {
            console.log(resMenu)
        })
    }
})

实现co方法解决手动调用的问题。


function co(fn) {
    const iterator = fn()
    function handleResult(result) {
        //已经迭代完了直接返回,没有迭代完递归调用
        if (result.done) return
        result.value
            .then((res) => {
                handleResult(iterator.next(res))
            })
            .catch((err) => {
                console.log(err)
            })
    }
    //默认执行一次
    handleResult(iterator.next())
}
co(getInfos)

1690953512696.png

上面代码使用递归实现依次调用问题

使用生成器实现async/await

使用async/await


async function fetchInfos() {
    const res = await getUsers()
    console.log(res)
    const resMenu = await getMenus()
    console.log(resMenu)
}
fetchInfos()
//打印结果:
{
  status: true,
  list: [ { name: 'hp', age: 18 }, { name: 'zhh', age: 18 } ]
}
{
  status: true,
  list: [ { name: '一级菜单-1', pid: 0 }, { name: '一级菜单-2', pid: 0 } ]
}

关于使用generator实现async/await可以参考这篇文章,讲的很详细