• javascript异步编程之generator(生成器函数)与asnyc/await语法糖


    Generator 异步方案

    相比于传统回调函数的方式处理异步调用,Promise最大的优势就是可以链式调用解决回调嵌套的问题。但是这样写依然会有大量的回调函数,虽然他们之间没有嵌套,但是还是没有达到传统同步代码的可读性。如果以下面的方式写异步代码,它是很简洁,也更容易阅读的。

    // like sync mode
    
    try{
      const value1 = ajax('/api/url1')
      console.log(value1)
      const value2 = ajax('/api/url1')
      console.log(value2)
      const value3 = ajax('/api/url1')
      console.log(value3)
      const value4 = ajax('/api/url1')
      console.log(value4)
      const value5 = ajax('/api/url1')
      console.log(value5)
    }catch(err){
      console.log(err)
    }
      
    

    ES2015提供了生成器函数(Generator Function)它与普通函数的语法差别在于,在function语句之后和函数名之前,有一个“*”作为生成器函数的标示符。

    在我们去调用生成器函数的时候他并不会立即去执行这个函数,而是会得到一个生成器对象,直到我们手动调用对象的next 方法,函数体才会开始执行,我们可以使用关键字yield去向外返回一个值,我们可以在next方法的返回值中去拿到这个值。另外再返回的属性中还有一个done关键字来表示生成器是否执行完了,

    yield不会像return一样去结束函数的执行,只是暂停函数的执行,直到外接下一次调用next方法时才会继续从yield位置往下执行

    function * foo () {
      console.log('start')
    	yield 'foo'
    }
    
    const generator = foo()
    
    const result = generator.next()
    

    调用next方法的时候传入了参数的话,所传入的参数会作为yield关键字的返回值

    function * foo () {
      console.log('start')
    	// 我可以在这里接收next传入的参数
    	const res = yield 'foo'
      console.log(res) // 这是我传入的参数
    }
    
    const generator = foo()
    
    const result = generator.next('这是我传入的参数')
    console.log(result) // { value: 'foo', done: false }
    

    如果我们调用了生成器函数的throw方法,这个方法会给生成器函数内部抛出一个异常

    function * foo () {
      console.log('start')
      // 我可以在这里接收next传入的参数
      try {
        const res = yield 'foo'
        console.log(res) // 这是我传入的参数
      } catch (err) {
        console.log(err.message) // 抛出错误
      }
    }
    
    const generator = foo()
    
    const result = generator.next('这是我传入的参数')
    console.log(result)
    
    generator.throw(new Error('抛出错误'))
    

    利用生成器函数和Promise来实现异步编程的体验

    function ajax(url) {
      return new Promise((resove, reject) => {
        var xhr = new XMLHttpRequest()
        xhr.open('GET', url)
        // 新方法可以直接接受一个j对象
        xhr.responseType = 'json'
        xhr.onload = function () {
          if (this.status === 200) {
            resove(this.response)
          } else {
            reject(new Error(this.statusText))
          }
        }
        xhr.send()
      })
    }
    
    function* main() {
      const user1 = yield ajax('/json1.json')
      console.log(user1)
    
      const user2 = yield ajax('/json2.json')
      console.log(user2)
    
      const user3 = yield ajax('/json3.json')
      console.log(user3)
    }
    
    const g = main()
    const result = g.next()
    
    result.value.then(data => {
      const result2 = g.next(data)
    
      if (result2.done) return
      result2.value.then(data2 => {
        const result3 = g.next(data2)
    
        if (result3.done) return
        result3.value.then(data3 => {
          g.next(data3)
        })
      })
    })
    

    很明显生成器的执行器可以使用递归的方式去调用

    const g = main()
    
    function handleResult(result) {
      if (result.done) return
      result.value.then(data => {
        handleResult(g.next(data))
      }, err => {
        g.throw(err)
      })
    }
    
    handleResult(g.next())
    

    生成器函数的调用其实都是差不多的,所以我们可以写一个比较通用的执行器

    function co(generator) {
      const g = generator()
    
      function handleResult(result) {
        if (result.done) return
        result.value.then(data => {
          handleResult(g.next(data))
        }, err => {
          g.throw(err)
        })
      }
    
      handleResult(g.next())
    }
    
    
    co(main)
    

    当然这样的执行器在社区中已经有一个比较完善的库了co。这种co的方案在2015年之前是特别流行的,后来在出了async/await语法糖之后,这种方案相对来讲就没有那么普及了。使用generator这种方法最明显的变化就是异步调用回归到扁平化了

    async/await

    有了generator之后js异步编程基本上与同步代码有类似的体验了,但是使用generator这种异步方案还需要自己手动去写一个执行器函数,会比较麻烦。在ES2017的版本中新增了一个叫做async的函数,它同样提供了这种扁平化的编程体验,并且是语言层面的标准的异步编程语法。其实async函数就是生成器函数更方便的语法糖,所以语法上给generator函数是类似的。

    async function main() {
      try {
        const user1 = await ajax('/json1.json')
        console.log(user1)
    
        const user2 = await ajax('/json2.json')
        console.log(user2)
    
        const user3 = await ajax('/json3.json')
        console.log(user3)
      } catch (error) {
        console.log(error)
      }
    }
    
    main()
    

    async 函数返回一个Promise对象,更利于对整体代码控制

    promise.then(() => {
      console.log('all completed')
    }).catch(err => {
      console.log(err)
    })
    

    原文地址: https://kspf.xyz/archives/21
    更多内容微信公众号搜索充饥的泡饭
    小程序搜一搜开水泡饭的博客

  • 相关阅读:
    mars3d实现禁止地图移动,禁止地图左右平移,但是鼠标可以移动的效果。
    文本向量化
    LocalDateTime的使用
    2022-08-29 第六小组 瞒春 学习笔记
    皕杰报表中填报控件显示模式控制问题
    docker安装以及部署
    《opencv学习笔记》-- 寻找已知物体
    十四、流式编程(1)
    java计算机毕业设计消防网站源码+系统+数据库+lw文档+mybatis+运行部署
    计算模型参数量
  • 原文地址:https://www.cnblogs.com/kspf/p/16849461.html