来源:FE研发 - 侯晓迪
为什么要有Event Loop?
- 因为Javascript设计之初就是一门单线程语言,因此为了实现主线程的不阻塞,Event Loop这样的方案应运而生。
输出结果?
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
Promise.resolve().then(() => {
console.log(3)
}).then(() => {
console.log(4)
})
console.log(5)
1 5 3 4 2
宏任务 macrotask
- setTimeout
- setInterval
- I/O
- requestAnimationFrame (浏览器独有)
- UI rendering (浏览器独有)
- setImmediate (Node独有)
微任务 microtask
- Promise
- MutationObserver 会在指定的DOM发生变化时被调用
- process.nextTick (Node独有)
EventLoop 在做什么?
- 执行最旧的task(一次)
- 检查是否存在microtask,然后不停执行,直到清空队列(多次)
- 执行render
LV2
// requestAnimationFrame(()=>console.log('1111'))
// requestIdleCallback(()=>console.log('2222'))
console.log(1);
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3)
});
});
new Promise((resolve, reject) => {
console.log(4)
resolve(5)
}).then((data) => {
console.log(data);
}).then(() => {
console.log(6);
})
setTimeout(() => {
console.log(7);
})
console.log(8);
1 4 8 5 6 2 3 7
注意await
const one = () => Promise.resolve(1)
async function fn() {
console.log(2)
const res = await one()
console.log(res)
}
new Promise((resolve, reject) => {
console.log(3)
resolve()
}).then(() => {
console.log(4)
})
console.log(6)
fn()
console.log(7)
3 6 2 7 4 1
Node 中的 EventLoop
- timers:执行setTimeout() 和 setInterval()中到期的callback。
- I/O callbacks:上一轮循环中有少数的I/Ocallback会被延迟到这一轮的这一阶段执行
- idle, prepare:仅内部使用
- poll:最为重要的阶段,执行I/O callback,在适当的条件下会阻塞在这个阶段
- check:执行setImmediate的callback
- close callbacks:执行close事件的callback,例如socket.on("close",func)
- process.nextTick() 在各个阶段切换的中间执行
为什么使用requestAnimationFrame
window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。回调函数会在浏览器下一次重绘之前执行。
事件循环不一定每轮都伴随着重渲染,但是一定会伴随着微任务执行。
requestIdleCallback
在渲染屏幕之后执行,并且是否有空执行要看浏览器的调度,如果你一定要它在某个时间内执行,请使用 timeout参数。