如果面试官问你 JS中的event loop
是什么?我相信大多数人都能答出来JS是单线程语言,只有一个主线程执行,执行栈,同步、异步之类。但是,这样的理解只是浅层的,如果面试官要你再深入解释,我相信大多数人都卡住了,不知道还能解释什么,那么,问题来了,这种情况怎么办?
来看这篇文章,看完了你就知道怎么办了。
JS页面的任务不仅可以按照同步异步来分,也可以分为macro-task
和micro-task
macro-task都有:包括整体代码script,setTimeout,setInterval
micro-task都有:Promise,process.nextTick
页面初始化时,不同的任务会进入到不同的 Event Queue(事件队列)
初始化时,会执行所有代码,将setTimeout等加入到macro-task
的事件列表,将Promise等加入到micro-task
事件列表。
遇到立即执行的代码,则立即执行,之后,会将事件列表中所有的micro-task
都执行完毕。
第一轮事件循环结束。
第二轮事件循环开始,拿出第一轮中第一个放入macro-task
列表中的事件开始执行完毕
第二轮事件循环结束。
第三轮循环开始,拿出第一轮中第二个放入macro-task列表中的事件开始执行完毕
第三轮事件循环结束。
就像上面这样,反复执行,即事件循环。
看了上面的解释,接下来,我们来看一个例子:
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
我们按照上面的理论来分析这段代码:
首先,进入到第一轮事件循环:
遇到 console.log('1') 输出 '1'
遇到 setTimeout,将其回调函数加入到 macro-task
事件列表,记为 setTimeout1
遇到 process.nextTick,将其回调函数加入到 micro-task
事件列表,记为 process1
遇到 new Promise,立即执行,输出 ** '7'**,then被分发到 micro-task
事件列表,记为 then1
遇到 setTimeout 将其回调函数加入到 macro-task
事件列表,记为 setTimeout2
执行到这里,我们来数数第一轮事件循环中的任务列表:
macro-task:setTimeout1,setTimeout2
micro-task:process1, then1
将列表中所有的 micro-task 执行完毕,输出 '6','8'
第一轮事件循环正式结束。
第二轮事件循环开始:
这时,第一轮输出结果是 '1','7','6','8'
在 macro-task
中,拿出第一个进入的事件,即setTimeout1,将其推入到执行栈开始执行
先输出:'2'
遇到 process.nextTick,将其回调函数加入到 micro-task
事件列表,记为 process2
遇到 new Promise, 立即执行,输出 ** '4'**,将then回调函数加入到 micro-task
事件列表,记为 then2
执行到这里,我们数数第二轮事件循环中的任务列表:
macro-task:setTimeout1
micro-task:process2,then2
将列表中所有的 micro-task 执行完毕,输出:'3', '5'
第二轮事件循环结束
第三轮事件循环开始,前两轮输出为:'1','7','6','8','2','4','3', '5'
在 macro-task 中,拿出第二个进入的事件,即setTimeout2,将其推入到执行栈开始执行
先输出:'9'
遇到 process.nextTick,将其回调函数加入到 micro-task
事件列表,记为 process3
遇到 new Promise, 立即执行,输出 '11',将then回调函数加入到 micro-task
事件列表,记为 then3
执行到这里,我们数数第三轮事件循环中的任务列表:
macro-task:setTimeout2
micro-task:process3,then3
将列表中所有的 micro-task
执行完毕,输出:'10', '12'
第三轮事件循环结束。
如果还有第四轮,第五轮,则循环上面的步骤,这样的循环,即事件循环。
总结执行结果:'1','7','6','8','2','4','3','5','9','11','10','12'
好,写完收工,等待下班去吃大餐。
原文作者写的更好,大家可以看看 https://juejin.im/post/59e85eebf265da430d571f89