其实关于JS的事件循环,自己之前是有部分了解的,但是这个了解还不够详细和深刻。今天看到一个有趣的JS题目,由此引发了自己对于这一块知识的深入学习和梳理。
题目如下:
setTimeout(function(){
console.log(1);
},0)
new Promise(function(resolve) {
console.log(2);
resolve();
}).then(function() {
console.log(3)
})
console.log(4);
问具体的输出结果?正确的解答这道题,你必须明白JS中的两个概念 -事件循环和宏任务、微任务。
我们先来看js中的事件循环,这里盗用一张图
导图要表达的内容用文字来表述的话:
- 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
- 当指定的事情完成时,Event Table会将这个函数移入Event Queue。
- 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
- 上述过程会不断重复,也就是常说的Event Loop(事件循环)。
广义的同步任务和异步任务,我们对任务有更精细的定义:
- macro-task(宏任务):包括整体代码script,setTimeout,setInterval
- micro-task(微任务):Promise,process.nextTick
有了上面这些基础知识,下面进行上面代码的详细
- 这段代码作为宏任务,进入主线程。
- 先遇到setTimeout,那么将其回调函数注册后分发到宏任务Event Queue。(注册过程与上同,下文不再描述)
- 接下来遇到了Promise,new Promise立即执行,then函数分发到微任务Event Queue。
遇到console.log(),立即执行。 - 好啦,整体代码script作为第一个宏任务执行结束,看看有哪些微任务?我们发现了then在微任务Event Queue里面,执行。
- ok,第一轮事件循环结束了,我们开始第二轮循环,当然要从宏任务Event Queue开始。我们发现了宏任务Event Queue中setTimeout对应的回调函数,立即执行。
- 结束
这里还有一个重要的知识点,那就是事件循环,宏任务,微任务的关系,如图所示:
下面是我自己的总结。
JS是单线程的,它是通过事件循环机制来实现异步的。
首先是主线程执行当前栈中的任务,遇到同步任务继续执行,遇到异步任务会将其放入Event Tables的事件队列中。Event Tables的事件队列又分为两类,一类是宏任务,一类是微任务。
当主线程的任务执行完毕后,回去任务队列中取出任务来执行。此时会优先选取任务队列中的微任务,执行完微任务后,才会去取宏任务执行。
对了,忘记说了,正确输出是2,4,3,1。你做对了吗?
本文参考自以下文章: