阅读 1937

用动画演示宏任务与微任务运行过程

有一道面试题,关于宏任务和微任务的基础面试题,代码如下

console.log('1');                           // 1            
setTimeout(function() {						// 2
    console.log('2');						// 3
    new Promise(function(resolve) {         // 4
        console.log('3');                   // 5
    	resolve();							// 6
    }).then(function() {   					// 7
        console.log('4')					// 8
    })
})
new Promise(function(resolve) {				// 9
    console.log('5');						// 10
    resolve();								// 11
}).then(function() {						// 12
    console.log('6')						// 13
})
console.log('10')							// 14
复制代码

答案是:1,5,10,6,2,3,4
因为看到很多文章讲宏任务和微任务都是以文字和图片的形式讲解,这一次我试图通过动画的方式完整演绎整个运行过程
运行过程如下:

  1. 代码从上到下开始运行,这是一段代码块,会作为一个宏任务进入到任务队列中,然后事件循环在轮询的时候,会执行这段代码,暂且叫做全局任务(因为在全局作用域执行的)。首先执行第1行(代码中标识的“//1”)打印1
  2. 执行到第2行的时候,发现这是一个setTimeout方法,js引擎会把setTimeout回调函数放进延迟队列中。是的,浏览器不是只有一个队列,还有其他队列,其中一个就是延迟队列,专名放像setTimeout, setInterval这些延迟任务的,其他还放浏览器内部的延迟任务。
  3. 接下来执行到第9行,promise,这是一个微任务。首先会马上执行传入promise函数里面的代码(有兴趣的同学可以了解一下Promise规范,然后自己实现一个Promise,参考这篇文章)。执行第10行,打印5
  4. 当执行第11行遇到resolve()的时候,会把传进then方法里面的回调函数放进微任务队列中。

这里暂时插入一个知识,其实每一个宏任务中都会带有微任务队列,来放微任务的,常见的微任务就是Promise,MutationObserver。当前宏任务中的代码执行完毕后,继续执行微任务队列中的任务。
5. 然后执行最后一行,第14行,打印10
6. 上面我们说了,当前宏任务执行完毕后,会去看微任务队列中有没有微任务。而之前有一个微任务,也就是第13行的代码。所以这个时候会打印6
7. 执行到这里,全局任务这个宏任务就执行完毕了,事件循环就取下一个宏任务,这个时候会去延迟队列中取任务。事件循环会在任务队列,延迟队列来回检查队列中的任务。如果发现setTimeout的定时结束,就会马上执行延迟队列中的到期宏任务,否则继续去任务队列中检查是否有新任务。发现延迟队列有一个任务,这个时候就执行第3行,打印2。 8. 然后又遇到promise,一样的,先执行传入的函数,执行第5行,打印3。 9. 这时遇到了resolve,照样会把then里面的回调函数放进微任务队列中。别忘了前面讲的,每一个宏任务中都有一个微任务队列,setTimeout属于宏任务。宏任务执行完毕后就去执行微任务,执行第8行,打印4。微任务执行完毕后,当前宏任务就退出事件循环。如果执行过程中,又产生了微任务,继续放进微任务队列中,直到微任务清空,才会执行下一个宏任务。

文章分类
前端
文章标签