zoukankan      html  css  js  c++  java
  • async和await是如何实现异步编程?

    目录

    • 异步编程样例
    • 样例解析
    • 浅谈Promise如何实现异步执行
    • 参考

    1.异步编程样例

    样例:

    // 等待执行函数
    function sleep(timeout) {
      return new Promise((resolve) => {
        setTimeout(resolve, timeout)
      })
    }
    
    // 异步函数
    async function test() {
      console.log('test start')
      await sleep(1000)
      console.log('test end')
    }
    
    console.log('start')
    test()
    console.log('end')
    

    执行结果:

    start
    test start
    end
    test end
    

    2.样例解析

    在样例代码中,test异步函数使用了asyncawait语法,这是ES2017里面的异步编程规范。而为了在较低版本的浏览器或Node支持这种语法,其中一种解决方案是将其转化为Generator函数和Promise来实现。换句话说,任何的asyncawait实现的异步函数,都可以替换成Generator函数和Promise实现。

    第一步:先将asyncawait语法替换为相应的Generator 函数,如下

    // 代码结构完全一致,只是替换了对应关键字
    function *test() {
      console.log('test start')
      yield sleep(1000)
      console.log('test end')
    }
    

    第二步:为了执行Generator 函数,使用Promise实现一个自动执行器函数 spawn

    function spawn(genF) {
      return new Promise(function(resolve, reject) {
        const gen = genF();
        function step(nextF) {
          let next;
          try {
            next = nextF();
          } catch(e) {
            return reject(e);
          }
          if(next.done) {
            return resolve(next.value);
          }
          Promise.resolve(next.value).then(function(v) {
            step(function() { return gen.next(v); });
          }, function(e) {
            step(function() { return gen.throw(e); });
          });
        }
        step(function() { return gen.next(undefined); });
      });
    }
    

    第三步:将相应的Generator 函数和自动执行器函数相结合,即可得最终的等价代码

    function asyncTest() {
      spawn(test)
    }
    
    console.log('start')
    asyncTest()
    console.log('end')
    

    第四步:执行结果,与原来的一致

    start
    test start
    end
    test end
    

    第五步:分析asyncTest函数执行过程

    1. 执行到 spawn(test),进入spawn函数中,创建一个Promise并返回。
    2. 执行到 const gen = genF(),获取一个状态机。(Generator 函数是一个状态机,封装了多个内部状态。)
    3. 执行到 step(function() { return gen.next(undefined); }), 进入step 函数中。
    4. 执行到next = nextF()next等于gen.next(undefined)的返回结果。
      • gen.next(undefined)开始执行,状态机第一次调用,直到遇到第一个yield表达式为止,即yield sleep(1000),此时控制台先输出"test start",并且返回第一个状态{ value: reuslt, done: false }, 而 reuslt等于sleep(1000)返回的结果,其是一个Promise。
    5. 执行到if(next.done) ,此时第一个状态的donefalse,所以不执行if语句里面,继续往下执行。
    6. 执行到Promise.resolve(next.value),由于第一个状态的value是一个Promise,所以直接返回其本身,也就相当于执行sleep(1000).then(...),sleep函数异步等待1秒后,resolve接受的值为undefined,继续执行then方法。
    7. 执行到 step(function() { return gen.next(v); }),此时vundefined,再次进入step 函数中。
    8. 再次执行到next = nextF()next等于gen.next(undefined)的返回结果。
      • gen.next(undefined)再次执行时,状态机第二次调用,此时Generator函数已经执行完毕,此时控制台先输出"test end",并且返回最后的状态{ value: undefined, done: true }
    9. 执行到if(next.done) ,此时第一个状态的donetrue,所以执行if语句里面。
    10. 执行到return resolve(next.value),此时最初的Promise成功执行。
    11. 至此asyncTest函数执行结束。

    3.浅谈Promise如何实现异步执行

    从上述样例解析中可以看出,我们是用Promise来实现代码的异步执行,那Promise的内部是如何实现异步执行的呢?

    通过查看Promise源码实现,发现其异步执行是通过asap这个库来实现的。

    asapas soon as possible的简称,在Node和浏览器环境下,能将回调函数以高优先级任务来执行(下一个事件循环之前),即把任务放在微任务队列中执行。

    宏任务(macro-task)和微任务(micro-task)表示异步任务的两种分类。在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。

    用法:

    asap(function () {
        // ...
    });
    

    补充说明:这里提及的Promise源码并不是Node和浏览器的原生实现,是一个第三方库,仅以此为参考。


    4.参考

    ECMAScript 6 入门 - async 函数

    【翻译】Promises/A+规范

    Promise - Bare bones Promises/A+ implementation

  • 相关阅读:
    运行期优化
    虚拟机字节码执行引擎
    虚拟机类加载机制
    类文件结构
    垃圾收集机制
    浅析Spring MVC工作机制
    TomCat系统架构
    docker安装mongo初体验
    微服务笔记--概念
    使用maven构建多模块项目_记录
  • 原文地址:https://www.cnblogs.com/forcheng/p/12668387.html
Copyright © 2011-2022 走看看