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

2023-08-16  本文已影响0人  wo不是黄蓉

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

什么是生成器

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

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

有什么好处?

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

表现形式:

举个例子:


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中捕获不到异常

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

实现异步:

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)
})

打印结果:

现在的代码可以实现按照同步编码的方式实现异步编程,但是每次都需要手动调用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)

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

使用生成器实现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

function run(gen) {
  //把返回值包装成promise
  return new Promise((resolve, reject) => {
    var g = gen()

    function _next(val) {
      //错误处理
      try {
        var res = g.next(val) 
      } catch(err) {
        return reject(err); 
      }
      if(res.done) {
        return resolve(res.value);
      }
      //res.value包装为promise,以兼容yield后面跟基本类型的情况
      Promise.resolve(res.value).then(
        val => {
          _next(val);
        }, 
        err => {
          //抛出错误
          g.throw(err)
        });
    }
    _next();
  });
}



关于使用generator实现`async/await`可以[参考](https://juejin.cn/post/6844904096525189128#heading-13)这篇文章,讲的很详细
上一篇 下一篇

猜你喜欢

热点阅读