学习笔记之事件循环-Event loop

495 阅读2分钟

前言

本文仅以记录浏览器环境的event loop 和 node环境中的event loop的了解过程,如果错误,欢迎指正

浏览器环境

Event Loop是指在js执行环境中存在主执行线程和任务队列(Task Queue),其中所有同步任务都在主执行线程中形成一个执行栈,所有异步任务都会放到任务队列中

具体执行过程:
1. 主线程执行同步任务, 在主线程执行的过程中,不断行程堆栈并执行出栈入栈的操作
2. 如果主线程任务没有完成,继续完成,如果完成了就执行下一步
3. 系统读取队列任务, 开始执行
4. 不断循环

而我们的异步任务中,分为宏任务(macrotask) 和微任务(microtask),
执行顺序
1. 先取出macrotask任务队列中的第一个任务进行执行
2. 取出Macrotask Queue中一个任务执行。
3. 取出Microtask Queue中任务执行直到清空。



node

执行任务为:
1. timers 阶段: 这个阶段执行setTimeout和setInterval预定的callback;
2. I/O callbacks 阶段: 执行除了 close事件的callbacks、被timers设定的callbacks、setImmediate()设定的callbacks这些之外的callbacks;
3. idle, prepare 阶段: 仅node内部使用;
4. poll 阶段: 获取新的I/O事件, 适当的条件下node将阻塞在这里;
5. check 阶段: 执行setImmediate() 设定的callbacks;
6. close callbacks 阶段: 执行socket.on('close', ...)这些 callback




参考文档,对比 setImmediate 和 process.nextTick()

setImmediate 和 process.nextTick()

setImmediate(() => console.log('immediate1'));
setImmediate(() => console.log('immediate2'));

setTimeout(() => console.log('setTimeout1'), 1000);
setTimeout(() => {
    console.log('setTimeout2');
    process.nextTick(() => console.log('nextTick1'));
}, 0);
setTimeout(() => console.log('setTimeout3'), 0);

process.nextTick(() => console.log('nextTick2'));
process.nextTick(() => {
    process.nextTick(console.log.bind(console, 'nextTick3'));
});
process.nextTick(() => console.log('nextTick4'));
执行结果:

在控制台中执行node index.js,得到的结果如下:

nextTick2
nextTick4
nextTick3
setTimeout2
setTimeout3
nextTick1
immediate1
immediate2
setTimeout1
结论

在node中,nextTick的优先级高于setTimeout和setImmediate(),所以会先执行nextTick里面的信息打印。
但是对于嵌套的nextTick,会慢于同步的nextTick,所以nextTick4会先于nextTick3
然后开始一个Event Loop过程,首先执行timer阶段,而此时setTimeout所需要等待的时间是0,所以立即执行setTimeout2和setTimeout3里面的逻辑。而setTimeout1由于设置了执行时间,不满足执行条件,被放到下一轮Event Loop
当前Event Loop执行到check阶段,于是打印出immediate1、immediate2
执行后面的Event Loop,当setTimeout1达到执行条件时执行

基于node事件的event loop,我们使用事件也变得方便快捷

event.emit('eventType', () => {})
event.on('eventType', () => {})