相比浏览器中的事件循环, nodejs除了微任务和宏任务, 还有阶段性任务
| 浏览器 | nodejs事件循环 | |
|---|---|---|
| 运行结果 | ||
| 原理 | 基于浏览器 | 基于libuv |
| 核心 | 宏任务、微任务 | 宏任务、微任务 + 阶段性任务 |
Node.js 事件循环,定时器和 process.nextTick()
阶段性任务流程图(6个阶段)
1. timers , setTimeout/setInterval回调
2. pedding callbacks 系统回调
4. poll (复杂) 处理I/O
异步I/O
1. 事件循环的起点?
Node.js 事件循环的发起点有 4 个:
- Node.js 启动后;
- setTimeout 回调函数;
- setInterval 回调函数;
- 也可能是一次 I/O 后的回调函数。
2. 循环的是什么任务呢?
事件循环的主要包含微任务和宏任务

执行阶段主要处理三个核心逻辑:
- 同步代码。
- 将异步任务插入到微任务队列或者宏任务队列中。
- 执行微任务或者宏任务的回调函数。
3. 循环的任务是否存在优先级概念?
微任务在事件循环中优先级是最高的;优先将微任务队列清空,再执行宏任务队列
nodejs中的微任务:
- process.nextTick
- promise
优先级: process.nextTick > promise
nodejs中的宏任务:
- setTimeout
- setInterval
- setImmediate
- I/O
4. 什么进程或者线程来执行这个循环?
主要还是主线程来循环遍历当前事件
所以: 主线程会因为回调函数的执行而被阻塞**
5. 无限循环有没有终点?
当所有的微任务和宏任务都清空的时候,虽然当前没有任务可执行了,但是也并不能代表循环结束了。因为可能存在当前还未回调的异步 I/O,所以这个循环是没有终点的,只要进程在,并且有新的任务存在,就会去执行。**
6. Node.js 是单线程的还是多线程的?
主线程是单线程执行的,但是 Node.js 存在多线程执行,多线程包括 setTimeout 和异步 I/O 事件。其实 Node.js 还存在其他的线程,包括垃圾回收、内存优化等
作业题:
执行结果/nodejs中事件循环?
优先级
执行1
const fs = require('fs');setTimeout(() => {// 新的事件循环的起点console.log('1');}, 0);setImmediate(() => {console.log('setImmediate 1');});/// 将会在 poll 阶段执行fs.readFile('./index.html', { encoding: 'utf-8' }, (err, data) => {if (err) throw err;console.log('read file success');});/// 该部分将会在首次事件循环中执行Promise.resolve().then(() => {console.log('poll callback');});// 首次事件循环执行console.log('2');// 2// poll callback// 1// setImmediate 1// read file success
分析: setImmediate 会在 setTimeout 之后输出, 原因:
setTimeout 如果不设置时间或者设置时间为 0,则会默认为 1ms;
修改setTimeout的时间为1000ms , 定时器的回调函数执行顺序发生变化
// 2// poll callback// setImmediate 1// read file success// 1
执行2
const fs = require('fs');// 首次事件循环执行console.log('start');/// 将会在新的事件循环中的阶段执行fs.readFile('./index.html', { encoding: 'utf-8' }, (err, data) => {if (err) throw err;console.log('read file success');});setTimeout(() => {// 新的事件循环的起点console.log('setTimeout');}, 0);/// 该部分将会在首次事件循环中执行Promise.resolve().then(() => {console.log('Promise callback');});/// 执行 process.nextTickprocess.nextTick(() => {console.log('nextTick callback');});// 首次事件循环执行console.log('end');// start// end// nextTick callback// Promise callback// setTimeout// read file success
分析:
微任务队列包含:Promise.resolve 和 process.nextTick,宏任务队列包含:fs.readFile 和 setTimeout;
任务嵌套
执行
const fs = require('fs');setTimeout(() => {// 新的事件循环的起点console.log('1');fs.readFile('./config/test.conf', { encoding: 'utf-8' }, (err, data) => {if (err) throw err;console.log('read file sync success');});}, 0);/// 回调将会在新的事件循环之前fs.readFile('./config/test.conf', { encoding: 'utf-8' }, (err, data) => {if (err) throw err;console.log('read file success');});/// 该部分将会在首次事件循环中执行Promise.resolve().then(() => {console.log('poll callback');});// 首次事件循环执行console.log('2');// 2// poll callback// 1// read file success// read file sync success
分析:
主线程阻塞
执行
const fs = require('fs');setTimeout(() => {// 新的事件循环的起点console.log('1');sleep(10000);console.log('sleep 10s');}, 0);/// 将会在新的事件循环中的 pending callbacks 阶段执行fs.readFile('./test.conf', { encoding: 'utf-8' }, (err, data) => {if (err) throw err;console.log('read file success');});console.log('2');/// 函数实现,参数 n 单位 毫秒 ;function sleep(n) {var start = new Date().getTime();while (true) {if (new Date().getTime() - start > n) {// 使用 break 实现;break;}}}// 2// 1// // 阻塞了一会儿// sleep 10s// read file success
分析:
修改setTimeout的时间为10ms , 定时器的回调函数执行顺序发生变化
// 2// read file success// 1// sleep 10s

