相比浏览器中的事件循环, 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.nextTick
process.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