JS异步编程之generator与async/await语法糖详解

 更新时间:2022年11月02日 09:29:17   作者:开水泡饭  
这篇文章主要为大家详细介绍了JS异步编程中generator与async/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)
})

到此这篇关于JS异步编程之generator与async/await语法糖详解的文章就介绍到这了,更多相关JS generator async/await内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JavaScript实现淘宝购物件数选择

    JavaScript实现淘宝购物件数选择

    这篇文章主要为大家详细介绍了JavaScript实现淘宝购物件数的选择,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • js实现tab切换效果实例

    js实现tab切换效果实例

    这篇文章主要介绍了js实现的tab标签切换效果,功能非常简单,实现了点击切换的效果,推荐给大家,有需要的小伙伴可以参考下。
    2015-09-09
  • Three.js获取鼠标点击的三维坐标示例代码

    Three.js获取鼠标点击的三维坐标示例代码

    本篇文章主要介绍了Three.js获取鼠标点击的三维坐标示例代码。具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-03-03
  • element中的$confirm的使用

    element中的$confirm的使用

    这篇文章主要介绍了element中的$confirm的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04
  • 屏蔽相应键盘按钮操作

    屏蔽相应键盘按钮操作

    本篇文章主要是对屏蔽相应键盘按钮的操作代码进行了介绍,需要的朋友可以过来参考下,希望对大家有所帮助
    2014-03-03
  • Javascript UrlDecode函数代码

    Javascript UrlDecode函数代码

    将Url进行编码,前台JS需要使用这段内容,这时候就需要解码了
    2010-01-01
  • Javascript基础之数组的使用

    Javascript基础之数组的使用

    这篇文章主要介绍了Javascript基础之数组的使用的相关资料,介绍的非常详解,具有参考借鉴价值,感兴趣的朋友一起学习吧
    2016-05-05
  • Bootstarp风格的toggle效果分享

    Bootstarp风格的toggle效果分享

    这篇文章主要介绍了Bootstarp风格的toggle效果分享的相关资料,需要的朋友可以参考下
    2016-02-02
  • 微信小程序实现限制用户转发功能的实例代码

    微信小程序实现限制用户转发功能的实例代码

    这篇文章主要介绍了微信小程序实现限制用户转发的实例代码,通过截图加实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-02-02
  • 根据配置文件加载js依赖模块

    根据配置文件加载js依赖模块

    这篇文章主要介绍了根据配置文件加载js依赖模块,解决方法是笔者自己的思路,抛砖引玉,需要的朋友可以参考下
    2014-12-12

最新评论