美文网首页
2023.29 生成器函数以及使用生成器实现async/awai

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

作者: wo不是黄蓉 | 来源:发表于2023-08-16 13:45 被阅读0次

    大家好,我是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中捕获不到异常

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

    实现异步:

    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)这篇文章,讲的很详细
    

    相关文章

      网友评论

          本文标题:2023.29 生成器函数以及使用生成器实现async/awai

          本文链接:https://www.haomeiwen.com/subject/qywkmdtx.html