javascript是一门非阻塞单线程语言,执行js代码时,会创造一个执行环境,也就是执行上下文,不了解这部分的可以看一下这一篇博客
js基础 执行上下文栈
遇到异步任务的时候,会挂起,放到task队列中,等同步代码执行完毕后再回过头执行task中的任务。
console.log('script start')
setTimeout(function() {
console.log('setTimeout')
}, 0)
console.log('script end')
/*
output: script start---> script end ---> setTimeout
*/
虽然延时器设定时间为0,但是因为它是一个异步任务,所以依然按照上面的步骤执行,而且setTimeout默认的最小延迟时间为4ms,如果不足会自动补上。
微任务 宏任务
不同的任务源会被分配到不同的task中去,任务源可以分为微任务(microtask)成为 jobs 和 宏任务(macrotask)成为 task
- microtastk包括:
process.nextTick
,promise
,Object.observe
,MutationObserver
- macrotastk包括:
script
setTimeout
,setInterval
,setImmediate
,I/O
,UIRendering
console.log('script start')
setTimeout(function() {
console.log('setTimeout')
}, 0)
new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})
console.log('script end')
// script start => Promise => script end => promise1 => promise2 => setTimeout
很多人有个误区,认为微任务快于宏任务,其实是错误的。因为宏任务中包括了 script ,浏览器会先执行一个宏任务,接下来有异步代码的话就先执行微任务。
所以正确的一次 Event loop 顺序是这样的
- 执行同步代码,这属于宏任务
- 执行栈为空,查询是否有微任务需要执行
- 执行所有微任务
- 必要的话渲染 UI
- 然后开始下一轮 Event loop,执行宏任务中的异步代码
通过上述的 Event loop 顺序可知,如果宏任务中的异步代码有大量的计算并且需要操作 DOM 的话,为了更快的 界面响应,我们可以把操作 DOM 放入微任务中
Node中的Event loop
Node 的 Event loop 分为 6 个阶段,它们会按照顺序反复运行
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<──connections─── │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
参考链接 https://www.yuchengkai.cn/docs/frontend/browser.html#event-loop