Eventloop

264 阅读2分钟

来源: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

img

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参数。