异步的终极方案Async-Await 和Generator

Async-Await

基本介绍

之前解决异步我们一直使用Promise.then()方案,虽然解决了回调地狱的情况,但使用链式写法也并不特别优雅。比如看下面的代码。
在这里插入图片描述

所以就出现了一种号称异步的终极方案AsyncAwait。我们看他的定义

async 函数是使用async关键字声明的函数。async 函数是 AsyncFunction 构造函数的实例,并且其中允许使用
await 关键字。async 和 await 关键字让我们可以用一种更简洁的方式写出基于 Promise 的异步行为,而无需刻意地链式调用
promise。

我们可以把代码改写成下面这样:
在这里插入图片描述
通过上面的改写,我们可以使用同步的写法来实现异步编程。
需要注意的是,如果Promise是被reject的,需要通过try catch来捕获,代码如下所示:
在这里插入图片描述

注意事项

同一个async函数中的await前面的await先执行完成之后再执行后面的await,但不同async函数中的await是不会互相阻塞的。比如常见的就是forEach,我们看下面代码:

  const promise1 = () =>  new Promise((resolve, reject) => {
    setTimeout(() => resolve('promise1 val'), 1000)




  })









  const promise2 = () =>  new Promise((resolve, reject) => {
    setTimeout(() => resolve('promise2 val'), 1000)

  })









  const promise3 = () => new Promise((resolve, reject) => {

    setTimeout(() => resolve('promise3 val'), 1000)

  })








  const promiseList = [promise1, promise2, promise3];



  // 打印结果不符合预期,三个值在等待1s后同时打印出来了并没有依次执行
  promiseList.forEach(async promiseItem => {
    const val = await promiseItem()
    console.log(val);
  })

执行效果如下所示:
在这里插入图片描述
执行效果不符合预期的原因就是forEach中的三个await在三个不同的async函数中,是相互独立的,并不会await
我们将代码改成如下方式:

const promise1 = () => new Promise((resolve, reject) => {
    setTimeout(() => resolve('promise1 val'), 1000)




  })









  const promise2 = () => new Promise((resolve, reject) => {
    setTimeout(() => resolve('promise2 val'), 1000)

  })









  const promise3 = () => new Promise((resolve, reject) => {

    setTimeout(() => resolve('promise3 val'), 1000)

  })








  const promiseList = [promise1, promise2, promise3];




  // 输出符合预期
  // 先打印promise1 val等1s后打印promise2 val再等1s后打印promise3 val
  const fn = async () => {
    for (let i = 0; i < promiseList.length; i++) {
      // for循环的三个await 都在同一个async函数中
      // 所以第二个await 会等第一个await,第三个await会等第二个await依次执行
      const val = await promiseList[i]()
      console.log(val);
    }
  }
  fn()

执行效果如下所示:
在这里插入图片描述
打印效果符合预期,因为在这里三个await是在同一个async函数中的,所以会依次执行每一个await

Async的实现原理 – Generator

基本介绍

上面提到async 函数是 AsyncFunction 构造函数的实例async function* 声明定义了一个异步生成器函数,其返回一个 AsyncGenerator 对象。
比如说我们一般在代码中有三个方法分别获取一些后台的数据,如果我们使用async函数的话,一般这么写:

  const getAjaxData1 = () => new Promise((resolve, reject) => {
    setTimeout(() => resolve('promise1 val'), 1000)




  })









  const getAjaxData2 = () => new Promise((resolve, reject) => {


    setTimeout(() => resolve('promise2 val'), 2000)


  })









  const getAjaxData3 = () => new Promise((resolve, reject) => {


    setTimeout(() => resolve('promise3 val'), 3000)


  })




 const promiseFn = async () => {
    const val1 = await getAjaxData1()
    console.log('接受到的值1', val1)
    const val2 = await getAjaxData2()
    console.log('接受到的值2', val2)
    const val3 = await getAjaxData3()
    console.log('接受到的值3', val3)
 }
promiseFn()

效果如下所示:
在这里插入图片描述
其实async是会使用function * 生成一个函数异步生成器。如下如所示:而每一个yield后面的逻辑,都需要生成器函数调用next()方法执行,其返回值有两部分组成

  1. done:表当前函数生成器是否执行完,false表未执行完,true表执行完
  2. value:每个yield后面的返回值(在这里就是返回一个promise对象)
    在这里插入图片描述
    实际上通过function *获取promise的值完整逻辑是这样:
const getAjaxData1 = () => new Promise((resolve, reject) => {

    setTimeout(() => resolve('promise1 val'), 1000)




  })









  const getAjaxData2 = () => new Promise((resolve, reject) => {


    setTimeout(() => resolve('promise2 val'), 2000)


  })









  const getAjaxData3 = () => new Promise((resolve, reject) => {


    setTimeout(() => resolve('promise3 val'), 3000)


  })








  // generate执行每一个yield获取数据

  function* getAjaxDataStep() {

    yield getAjaxData1()

    yield getAjaxData2()

    yield getAjaxData3()

  }



  // 先执行 function* 一次,得到迭代器
  // promise实例通过.then方法获取promise的结果
  const myGenerate = getAjaxDataStep()
  myGenerate.next().value.then(val => {
    console.log('接受到的值1', val)
    return myGenerate.next().value
  }).then(val => {
    console.log('接受到的值2', val)
    return myGenerate.next().value
  }).then(val => {
    console.log('接受到的值3', val)
    return myGenerate.next().value
  }).then(val => {
    console.log('接受到的值4', val)
    return myGenerate.next().value
  })

其执行效果如下:
在这里插入图片描述
可以看到和上面使用Promise执行得到的效果一样。

实现一个自动执行的Generator

我们如果不使用上面async await或者是Promise 对象.then()的方式,我们也可以封装一个函数,自动执行function *中的每一个任务,来获取每个值

const getAjaxData1 = () => new Promise((resolve, reject) => {

    setTimeout(() => resolve('promise1 val'), 1000)




  })









  const getAjaxData2 = () => new Promise((resolve, reject) => {


    setTimeout(() => resolve('promise2 val'), 2000)


  })









  const getAjaxData3 = () => new Promise((resolve, reject) => {


    setTimeout(() => resolve('promise3 val'), 3000)


  })








  // generate执行每一个yield获取数据

  function* getAjaxDataStep() {

    yield getAjaxData1()

    yield getAjaxData2()

    yield getAjaxData3()

  }



  // 一个高阶函数,接受function * 为入参
  const autoRunGenerateFn = (genFn) => {
    // 得先执行一次,得到迭代函数
    const myGen = genFn()
    let count = 1;
    // 递归执行该函数
    // 如果done: false就已知执行autoRun
    // 递归出口就是done: true
    const autoRun = () => {
      const result = myGen.next()
      const done = result.done
      const value = result.value
      if (done) {
        // 是否已近完成:完成递归出口
        console.log('已完成:', value);
      } else {
        // 是否已近完成:未完成 
        if (value instanceof Promise) {
          // promise的值
          value.then(v => {
            // promise的值通过.then获取
            console.log(`接受到的值${count}`, v)
            count++
            // 未全部完成,在promise.then内再次autoRun
            autoRun()
          })
        } else {
          // 非promise其值可直接获取
          console.log(`接受到的值${count}`, v)
          count++
          // 未全部完成,再次autoRun
          autoRun()
        }
      }
    }
    autoRun()
  }

  autoRunGenerateFn(getAjaxDataStep)

实现效果如下所示:
在这里插入图片描述

参考资料

Async
function *

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MY9WlCJI' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片