美文网首页
EventLoop - 宏任务&微任务 - 普通题目

EventLoop - 宏任务&微任务 - 普通题目

作者: 木头就是我呀 | 来源:发表于2020-04-07 18:09 被阅读0次

近几天看了好多js基础原理相关的知识。同步代码&异步代码的执行顺序(浏览器的事件循环&宏任务&微任务)一直是面试的高频考题,这里总结一下自己做这种题的思路(流程)。

JS代码中的异步任务可进一步分为宏任务(macrotask)与微任务(microtask)。
宏任务包括:script代码、setTimeout、setInterval、I/O、UI render
微任务包括:promise.then、Object.observe(已废弃)、MutationObserver

  • 看了很多文章,网上对于宏微任务的执行顺序有一点点不一致,我更倾向于下面的版本。
    遇到异步任务的时候,先判断是哪种任务,然后追加到相应的任务队列中。当执行同步代码时,遇到宏微任务时就将其添加到对应的队列中;当同步代码执行完毕,浏览器会先去清空微任务队列:依次取出微任务,并执行,执行过程中如果出现新的微任务,就继续添加到 当前 微任务队列的末尾,如果遇到同步任务,就添加到同步任务的队列末尾;当微任务清空之后,浏览器会从宏任务中取出队首任务并执行,在执行过程中,遇到微任务就追加到微任务的队列末尾,如果遇到宏任务,就追加到宏任务的队列末尾,当此宏任务执行完毕,马上去清空当前微任务的队列,如此反复,直至宏任务队列 & 微任务队列全部清空。

    EventLoop循环 (图片来自https://juejin.im/post/5e01e0f76fb9a01622779fad)
  • 了解执行原理后,就做一些题,锻炼一下:

  • 题目1 - 较简单

 setTimeout(function(){ //s1
   console.log('1')
 });
 new Promise(function(resolve){ // p1-resolve
   console.log('2');
   resolve();
 }).then(function(){ // p1-then
   console.log('3')
 });
 console.log('4');

// 输出 2 4 3 1

步骤:
1. 主代码执行
宏任务队列 = [s1]
进入Promise声明,console.log('2');  ======================> 2
微任务队列 = [p1-then]
console.log('4');  ======================> 4

2. 清掉微任务队列
console.log('3')  ======================> 3
微任务队列 = []

3. 拿出宏任务队列第一项 s1
宏任务队列 = []
console.log('1')  ======================> 1
该宏任务结束
  • 题目2 - 稍复杂一些
console.log(1);
setTimeout(() => { // s1
   console.log(2);
   Promise.resolve().then(() => { // p1-then
     console.log(3);
   });
 }, 0);
 new Promise((resolve, reject) => { // p2-resolve
   console.log(4);
   resolve(5);
 }).then((data) => { // p2-then
   console.log(data);
 });
 setTimeout(() => { // s2
   console.log(6);
 }, 0);

// 输出 1 4 5 2 3 6

步骤:
1. 主代码执行
console.log(1); ======================> 1
宏任务队列 = [s1]
进入Promise声明   ======================> 4
微任务队列 = [p2-then]
宏任务队列 = [s1,s2]

2. 清掉微任务队列
console.log(data); ======================> 5
微任务队列 = []

3. 拿出宏任务队列中第一项 s1
宏任务队列 = [s2]
console.log(2); ======================> 2
微任务队列 = [p1-then]
该宏任务结束

4. 清空微任务队列
console.log(3); ======================> 3
微任务队列 = []

5. 拿出宏任务队列中第一项 s2
宏任务队列 = []
console.log(6); ======================> 6
该宏任务结束
  • 题目3 - 有await,await后续作为Promise.then即可
 async function async1() {
   console.log(1); 
   const result = await async2(); 
   console.log(3);  // p-await
 } 
 async function async2() { 
   console.log(2); 
 } 
 Promise.resolve().then(() => {  // p1
   console.log(4); 
 }); 
 setTimeout(() => {  // s1
   console.log(5); 
 }); 
 async1(); 
 console.log(6);

// 输出 1 2 6 4 3 5

步骤:
1. 主代码执行
微任务队列 = [p1]
宏任务队列 = [s1]
console.log(1) ======================> 1
console.log(2) ======================> 2
await方法返回的是一个promise对象,因此await方法执行完毕的后续代码都应该归入微任务队列,
所以console.log(3)应该作为微任务,追加到微任务队列中。
微任务队列 = [p1, p-await]
console.log(6); ======================> 6

2. 清空微任务队列
console.log(4);  ======================> 4
console.log(3);  ======================> 3
微任务队列 = []

3. 拿出宏任务队列第一项 s1
宏任务队列 = []
console.log(5);  ======================> 5
  • 题目4 - 较复杂
 setTimeout(() => { // s1
   console.log('setTimeout - 1')
   setTimeout(() => { // s1-1
     console.log('setTimeout - 1 - 1')
   })
   new Promise(resolve => { // p1-resolve
     console.log('setTimeout - 1 - resolve')
     resolve() 
   }).then(() => { // p1-then
     console.log('setTimeout - 1 - then')
     new Promise(resolve => resolve()).then(() => { // p1-then-then
         console.log('setTimeout - 1 - then - then')
     })
   })
 })
 setTimeout(() => { // s2
   console.log('setTimeout - 2')
   setTimeout(() => { // s2-1
     console.log('setTimeout - 2 - 1')
   })
   new Promise(resolve => resolve()).then(() => { // p2-then
     console.log('setTimeout - 2 - then')
     new Promise(resolve => resolve()).then(() => { // p2-then-then
         console.log('setTimeout - 2 - then - then')
     })
   })
 })

// 输出
// setTimeout - 1
// setTimeout - 1 - resolve
// setTimeout - 1 - then
// setTimeout - 1 - then - then 
// setTimeout - 2
// setTimeout - 2 - then
// setTimeout - 2 - then - then
// setTimeout - 1 - 1
// setTimeout - 2 - 1

步骤: 
1. 主代码执行
宏任务队列 = [s1 , s2]

2. 拿出第一个宏任务 s1
宏任务队列 = [s2]
console.log('setTimeout - 1') ======================> setTimeout - 1
宏任务队列 = [s2 , s1-1]
console.log('setTimeout - 1 - resolve') ======================> setTimeout - 1 - resolve
微任务队列 = [p1-then]
s1宏任务结束

3. 清空微任务队列
微任务队列 = []
console.log('setTimeout - 1 - then') ======================> setTimeout - 1 - then
微任务队列 = [p1-then-then]

4. 清空微任务队列
微任务队列 = []
console.log('setTimeout - 1 - then - then') ======================> setTimeout - 1 - then - then

5. 拿出第一个宏任务 s2
宏任务队列 = [s1-1]
console.log('setTimeout - 2') ======================> setTimeout - 2
宏任务队列 = [s1-1 , s2-1]
微任务队列 = [p2-then]

6. 清空微任务队列
微任务队列 = []
console.log('setTimeout - 2 - then') ======================> setTimeout - 2 - then
微任务队列 = [p2-then-then]

7. 清空微任务队列
微任务队列 = []
console.log('setTimeout - 2 - then - then') ======================> setTimeout - 2 - then - then

8. 拿出宏任务队列第一项 s1-1
宏任务队列 = [s2-1]
console.log('setTimeout - 1 - 1') ======================> setTimeout - 1 - 1

9. 拿出宏任务队列第一项 s2-1
宏任务队列 = []
console.log('setTimeout - 2 - 1') ======================> setTimeout - 2 - 1

题目参考自:
https://juejin.im/post/5e01e0f76fb9a01622779fad

相关文章

网友评论

      本文标题:EventLoop - 宏任务&微任务 - 普通题目

      本文链接:https://www.haomeiwen.com/subject/ttinphtx.html