Node.js事件循环工作原理
- 宏观任务队列(Macro Task Queue):Node.js中有多个宏任务队列,例如定时器队列(
setTimeout
、setInterval
)、setImmediate
队列等。事件循环首先从宏任务队列中取出一个任务并执行。
- 执行栈:取出的宏任务会被放入执行栈中执行。在执行过程中,如果遇到同步代码,会直接在执行栈中执行;如果遇到异步操作(如I/O、定时器等),会将其交给相应的底层模块(如操作系统的I/O系统)处理,然后继续执行执行栈中的其他同步代码。
- 微任务队列(Micro Task Queue):当一个宏任务执行完毕后,事件循环会检查微任务队列(如
Promise.then
回调)。它会不断从微任务队列中取出任务并执行,直到微任务队列为空。
- 再次进入宏任务队列:微任务队列处理完毕后,事件循环会再次从宏任务队列中取出下一个任务,重复上述过程,如此循环往复。
与传统同步编程模型在处理I/O操作上的不同
- 传统同步编程:在传统同步编程模型中,当执行到I/O操作(如读取文件、网络请求等)时,程序会阻塞,等待I/O操作完成后才继续执行后续代码。这意味着在I/O操作等待期间,CPU处于空闲状态,无法执行其他任务,导致程序整体效率较低,特别是在处理大量I/O操作时,会严重影响性能。例如:
// 传统同步读取文件示例
const fs = require('fs');
const data = fs.readFileSync('file.txt', 'utf8');
console.log(data);
// 在readFileSync执行期间,后续代码无法执行
- Node.js事件驱动模型:在Node.js事件驱动模型中,I/O操作是异步的。当遇到I/O操作时,Node.js会将I/O任务交给底层模块处理,然后立即返回,继续执行后续代码。当I/O操作完成后,会将其回调函数放入相应的宏任务队列(如
setTimeout
类似,I/O完成后回调放入宏任务队列),等待事件循环取出执行。这样,CPU在I/O操作等待期间可以继续执行其他任务,大大提高了程序的并发处理能力和整体效率。例如:
// Node.js异步读取文件示例
const fs = require('fs');
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
// readFile调用后立即返回,后续代码可以继续执行