zoukankan      html  css  js  c++  java
  • async与await输出顺序的思考

    async

    1. async其实就是对Generator的封装,只不过async可以自动执行next()。

    2. async必须等到里面所有的await执行完,async才开始return,返回的Promise状态才改变。除非遇到return和错误。

    3. async默认返回一个Promise,如果return不是一个Promise对象,就会被转为立即resolve的Promise,可以在then函数中获取返回值。

    例一

    async function async1() {
        console.log("async1_start_2");
        await async2();
        console.log("async1_end_6");
        return 'async_return_8';
    }
     
    async function async2() {
        console.log("async2_3");
    }
     
    console.log("script_start_1");
     
    setTimeout(function() {
        console.log("setTimeout_9");
    }, 0);
     
    async1().then(function (message) { console.log(message) });
     
    new Promise(function(resolve) {
        console.log("promise_4");
        resolve();
    }).then(function() {
        console.log("promise_7");
    });
     
    console.log("script_end_5");
    

    输出为:

    image-20200830003450311

    这道题目考查的是我们对 事件循环 任务队列 的理解:

    事件循环(Event Loop):

    1. JS会首先判断代码是同步还是异步,同步进入主线程,异步进入任务队列;
    2. 同步任务进入主线程后一直执行,直到主线程空闲后,才会去任务队列中查看是否有可执行的异步任务,如果有就推入主线程中执行;
    3. 事件循环是一个先进先出(FIFO)队列,这说明回调是按照它们被加入队列的顺序执行的。

    [ 分析 ]:

    1. 在单线程的js中,异步代码会被放入一个事件队列,等到所有其他代码执行后再执行,而不会阻塞线程。我们从上到下看,首先打印:1

    2. setTimeout / setInterval 要放到任务队列的末尾,等待后续执行。继续往下走;

      此时的任务队列:

    • 宏任务队列: setTimeout
    • 微任务队列:none
    1. async1 开始执行,当函数里遇到await时,暂停执行(await所在行放在本次执行完),而 async1 函数 未完成部分被添加到宏任务队列;

      此时的任务队列:

    • 宏任务队列:async1, setTimeout
    • 微任务队列:none
    1. new Promise() 实例对象被new出来后,它里面的promise1会立刻打印,然后又遇到 then, 此时 promise 实例 被添加到微任务队列;

      此时的任务队列:

    • 宏任务队列:async1 ,setTimeout
    • 微任务队列: promise实例
    1. 接着打印:script end。至此,同步代码(第一个宏任务)已执行完毕。而我们的任务队列中还存在着 async1, promise对象, setTimeout异步回调;

    2. 由于异步代码第一次执行时,async1 函数 要早于 promise对象,所以紧接着 async1 函数继续执行没有执行完成的部分(例三中promise.then先于await,所以then先执行),执行完毕后,退出任务队列,打印:async1 end。然后把它的 then 逻辑添加到任务微任务队列中;

    此时的任务队列:

    • 宏任务队列:setTimeout

    • 微任务队列:promise实例 ,async1的then逻辑部分

    1. 先清空微任务队列,promise 实例 继续执行它的 then 的逻辑,打印:promise2。执行完毕后,退出微任务队列;

    此时的任务队列:

    • 宏任务队列:setTimeout

    • 微任务队列:async1的then逻辑部分

    1. async 函数执行 then 逻辑;

    此时的任务队列:

    • 宏任务队列:setTimeout

    • 微任务队列:none

    1. setTimeout是宏任务会在最后执行。

    例二

    console.log(1);
    async function asyncfn1(){
        console.log(2);
        await asyncfn2();
        console.log(5);
    };
    setTimeout(() => {
        console.log('setTimeout')
    }, 0)
     
    async function asyncfn2(){
        console.log(3)
    };
     
    asyncfn1();
    console.log(4);
    

    输出:

    1
    2
    3
    4
    5
    setTimeout
    

    细品:

    1. 首先执行全局同步代码,先输出 1
    2. 随后执行asyncfn1,输出 2 遇到awati后,先执行asyncfn2,将后面的代码放入宏任务队列,

    此时的任务队列:

    • 宏任务队列:asyncfn1剩余代码;
    • 微任务队列:none;
    1. 执行asyncfn2输出 3
    2. 继续执行全局同步代码,输出 4
    3. 执行宏任务输出 5

    例三

    var p = new Promise((res,rej) => {
      res('hello_6')
      console.log('1')
    })
    
    function hello() {
      console.log('hello_begins_2')
      return p
    }
    
    hello().then(res => {
      console.log(res)
      console.log('hello_7')
      return 'hello_10'
    }).then(res => {
      console.log(res)
      console.log('hello_11')
      return 'hello_13'
    }).then(res => {
      console.log(res)
      console.log('hello_14')
    })
    
    function test1 () {
      console.log('test1_5')
    }
    
    async function asy () {
      console.log('asy_begins_3')
      await console.log('asy_4')
    
      console.log('async_8')
      await console.log('asy_9')
    
      console.log('asy_ends_12')
    }
    
    asy()
    
    test1()
    

    结果:
    image-20200829235047575

    看官们可以根据输出结果细品;

    注意:await后面的代码虽然算作宏任务,但是和普通的微任务不在一个维度,位于更上一层的任务队列,所以优先度要比其他(下层)微任务要高;

    参考思路:

    1. 执行同步的全局代码输出 1(遇到new Promise()的需要立即执行)
    2. 11 行执行 hello函数,输出2,并返回一个Promise对象p,将hello函数的第一层then函数放入微任务队列;

    此时的任务队列:

    • 宏任务队列:none

    • 微任务队列:hello.then(0)

    1. 继续执行同步代码,到第38行,执行asy函数;
    2. 在第29行输出3,随后遇到await,执行该行,输出4,剩下的代码被放入了宏任务队列(为了区分任务的层次,标明了序号,先执行完同层的任务,再到其他层)

    此时的任务队列:

    • 宏任务队列:asy await后代码(0)

    • 微任务队列:hello.then(0),hello.then.then(1),hello.then.then.then(2)

    1. 继续执行同步全局代码,第40行,执行test函数,输出5
    2. 执行微任务队列中的hello.then,输出返回的promise对象p中的处理结果6(第12->8->2行),随后第13行输出7

    此时的任务队列:

    • 宏任务队列:asy await后代码(0)

    • 微任务队列:hello.then.then(1),hello.then.then.then(2)

    1. 执行第0层的宏任务,也就是asy await后的代码,第32行输出8,第39行遇到await,执行完该行输出9后,将后面的代码推进宏任务队列;

    此时的任务队列:

    • 宏任务队列:asy await后代码(1)

    • 微任务队列:hello.then.then(1),hello.then.then.then(2)

    1. 15行,执行hello函数的第二个then函数,返回处理结果res,输出1011

    此时的任务队列:

    • 宏任务队列:asy await后代码(1)

    • 微任务队列:hello.then.then.then(2)

    1. 同层的还有一个宏任务,执行asy await后的代码,第35行,输出12

    此时的任务队列:

    • 宏任务队列:none

    • 微任务队列:hello.then.then.then(2)

    1. 取出微任务队列中的最后一个任务,回到第19行输出hello的第二个then函数的处理结果,分别是1314

    至此程序执行完成;

  • 相关阅读:
    Linux命令总结--grep命令
    Linux命令总结--sed命令
    python函数--enumerate()方法
    python函数--index()方法
    在objc项目中使用常量的最佳实践
    iOS 开发 初级:应用内购买 In-App Purchase
    CFUUIDRef和CFStringRef-生成唯一标识符
    保留你的dSYM文件
    xcode 环境,多工程联编设置【转】
    ld: symbol dyld_stub_binding_helper not found, normally in crt1.o/dylib1.o/bundle1.o for architecture i386
  • 原文地址:https://www.cnblogs.com/AhuntSun-blog/p/13584169.html
Copyright © 2011-2022 走看看