当前位置:   article > 正文

第四题:关于异步问题的笔试题_async function async1(){ console.log('1'); await a

async function async1(){ console.log('1'); await async2();console.log('2') a

这是一道涉及事件循环的笔试题:

题目:

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}

async function async2() {
console.log('async2');
}

console.log('script start');

setTimeout(function() {
    console.log('setTimeout');
}, 0)

async1();

new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});

console.log('script end');

//  script start
//  async1 start
//  async2
//  promise1
//  script end'
//  async1 end
//  promise2
//  setTimeout
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

在分析这道题前先说说几个知识点:

任务队列
  • JS分为同步任务和异步任务
  • 同步任务都在主线程上执行,形成一个执行栈
  • 主线程之外,事件触发线程管理的一个任务队列
  • 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行
宏任务

(macro)task(又称为宏任务),可以理解是每次执行栈执行的代码就是一个宏任务。

浏览器为了能够使得JS内部(macro)task与DOM任务能够有序的执行,会在一个(macro)task执行结束之后,在下一个(macro)task执行开始前,对页面进行重新渲染:

    (macro)task -> 渲染 -> (macro) task ...
  • 1

(macro)task主要包括:

  • script(整体代码)
  • setTimeout
  • setInterval
  • I/O
  • UI事件
  • postMessage
  • MessageChannel
  • setImmediate(Node.js环境)
微任务

microtask(又称为微任务),可以理解是在当前task执行结束后立即执行的任务。也就是在当前task任务后,在下一个task之前,在渲染之前。

在某个宏任务执行之后,就会将在它执行期间产生的所有微任务都执行完毕。

microtask主要包括:

  • Promise.then
  • MutaionObserver
  • process.nextTick(Node.js环境)
运行机制

在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤如下:

  • 执行一个宏任务(栈中没有就从事件队列中获取)
  • 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
  • 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
  • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
  • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

流程图如下:
在这里插入图片描述

Promise和aysnc中的立即执行

Promise中的异步体现在then和catch中,所以写在Promise中的代码是被当做同步任务立即执行的。

async/await中,在出现await之前,其中的代码也是立即执行,在出现await时发生了什么?

await是一个让出线程的标志。await后面紧跟着的表达式会先执行,将接着await下面的代码加到micromask中,然后就会跳出整个 async函数执行后面的代码。

由于因为async await 本身就是promise+generator的语法糖。所以await后面的代码是microtask。所以对于本题中的:

    async function async1() {
        console.log('async1 start');
        await async2();
        console.log('async1 end');
    }

    等价于:

    async function async1() {
         console.log('async1 start');
         Promise.reselve(async2().then(() => {
             console.log('async1 end');
         }))
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
回到本题

了解上面的知识点后,对于本题就很简单了:

  • 首先进入第一个宏任务(script整体代码),遇到同步任务直接执行,即输出 ‘script start’

  • 遇到setTimeout将其放到宏任务队列

  • 遇到async1(),将async1函数里面的同步任务直接执行,输出‘async1 start’,然后将await紧跟着的表达式执行,执行async2(),输出‘async2’,最后将await后面的代码加入到微任务队列,跳出函数async1,执行下面的代码

  • 遇到Promise,将里面的同步任务立即执行,输出‘promise1’,将then后面的代码加入到微任务队列.

  • 最后立即执行console.log(‘script end’),输出‘script end’。

  • 到此,第一个宏任务结束,紧接着执行这一轮的微任务,依次执行微任务队列,输出‘async1 end’,再输出‘promise2’

  • 微任务执行完毕,接着执行下一个宏任务,这里只有setTimeout,执行它,输出‘setTimeout’

  • 结束流程

    变式一
      async function async1() {
          console.log('async1 start');
          await async2();
          console.log('async1 end');
      }
      async function async2() {
          //async2做出如下更改:
          new Promise(function(resolve) {
          console.log('promise1');
          resolve();
      }).then(function() {
          console.log('promise2');
      });
      }
      console.log('script start');
    
      setTimeout(function() {
          console.log('setTimeout');
      }, 0)
    
      async1();
    
      new Promise(function(resolve) {
          console.log('promise3');
          resolve();
      }).then(function() {
          console.log('promise4');
      });
    
      console.log('script end');
    
      // script start
      // async1 start
      // promise1
      // promise3
      // script end
      // promise2
      // async1 end
      // promise4
      // setTimeout
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
变式二
    async function async1() {
        console.log('async1 start');
        await async2();
        //更改如下:
        setTimeout(function() {
        console.log(' setTimeout1') // 第三个宏任务
    },0)
    }

    async function async2() {
        //更改如下:
        setTimeout(function() {
	    console.log('setTimeout2')
    },0)                // 第二个宏任务
    }

    console.log('script start');

    setTimeout(function() {
        console.log('setTimeout3');
    }, 0)   // 第一个宏任务

    async1();

    new Promise(function(resolve) {
        console.log('promise1');
        resolve();
    }).then(function() {
        console.log('promise2');
    });

    console.log('script end');

    //  script start
    //  async1 start
    //  promise1
    //  script end
    //  promise2
    // setTimeout3
    // setTimeout2
    // setTimeout1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
变式三
    async function a1 () {
        console.log('a1 start');
        await a2();
        console.log('a1 end');// 第二个微任务
    }
    async function a2 () {
        console.log('a2');
    }

    console.log('script start');

    setTimeout(() => {
        console.log('setTimeout')
    }, 0) // 第一个宏任务

    Promise.resolve().then(() => {
        console.log('promise1');
    }) // 第一个微任务

    a1();

    let promise2 = new Promise((resolve) => {
        resolve('promise2.then')
        console.log('promise2')
    })

    promise2.then((res) => {
        console.log(res)// 第三个微任务
        Promise.resolve().then(() => {
            console.log('promise3')//第4个微任务
        })
    })

    console.log('script end')
    // script start
    // a1 start
    // a2
    // promise2
    // script end
    // promise1
    // a1 end
    // promise2.then
    // promise3
    // setTimeout
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/555109
推荐阅读
相关标签
  

闽ICP备14008679号