拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Node.js入门教程(十五):事件循环

一、什么是事件循环事件循环是 Node.js 处理非阻塞 I/O 操作的核心机制使得单线程能够高效处理多个并发请求。Node.js 是基于单线程的 JavaScript 运行时利用事件循环来处理异步操作如文件读取、网络请求和数据库查询。事件循环使得 Node.js 能够非阻塞地运行代码、处理多个连接、以及执行异步 I/O 操作。事件循环使得 Node.js 能够处理大量并发的 I/O 操作而不会导致线程阻塞这是 Node.js 高效处理并发请求的关键。二、事件循环的阶段事件循环分为多个阶段每个阶段处理特定的任务。关键阶段如下┌───────────────────────────┐ ┌─│ Timers │ ← 执行 setTimeout() 和 setInterval() 的回调 │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ I/O Callbacks │ ← 处理一些延迟的 I/O 回调 │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ Idle, prepare │ ← 内部使用不常见 │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ Poll │ ← 检索新的 I/O 事件执行与 I/O 相关的回调 │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ Check │ ← 执行 setImmediate() 回调 │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ └──┤ Close Callbacks │ ← 处理关闭的回调如 socket.on(close, ...) └───────────────────────────┘各阶段说明阶段说明Timers执行setTimeout()和setInterval()的到期回调I/O Callbacks处理一些延迟的 I/O 回调如某些系统错误回调Idle, prepareNode.js 内部使用开发者一般不需要关心Poll检索新的 I/O 事件执行与 I/O 相关的回调核心阶段Check执行setImmediate()回调Close Callbacks处理关闭的回调如socket.on(close, ...)三、事件循环的流程任务进入事件循环队列事件循环按照阶段顺序进行处理每个阶段有自己的回调队列事件循环会在 poll 阶段等待新的事件到达如果没有事件会检查其他阶段的回调如果setImmediate()和setTimeout()都存在setImmediate()在 check 阶段先执行而setTimeout()在 timers 阶段执行示例setTimeout(() { console.log(Timeout callback); }, 0); setImmediate(() { console.log(Immediate callback); }); console.log(Main thread execution);输出顺序Main thread execution 先打印。 setImmediate() 和 setTimeout() 的执行顺序取决于当前事件循环的状态一般 setImmediate() 会先执行。四、宏任务与微任务宏任务MacroTasksetTimeout、setIntervalsetImmediateI/O 操作等微任务MicroTaskprocess.nextTickPromise.then执行顺序微任务优先级高于宏任务会在当前阶段的回调结束后立即执行。setTimeout(() { console.log(Timeout callback); }, 0); Promise.resolve().then(() { console.log(Promise callback); }); console.log(Main thread execution);执行输出结果Main thread execution Promise callback Timeout callback五、process.nextTick()process.nextTick()会在当前操作结束后、下一个阶段开始前执行微任务优先级高于 Promise。process.nextTick(() { console.log(Next tick callback); }); console.log(Main thread execution);输出Main thread execution Next tick callback执行优先级对比setTimeout(() console.log(setTimeout), 0); setImmediate(() console.log(setImmediate)); Promise.resolve().then(() console.log(Promise)); process.nextTick(() console.log(nextTick)); // 输出顺序 // nextTick // Promise // setTimeout 或 setImmediate取决于事件循环状态六、事件驱动程序在 Node.js 中事件驱动编程主要通过EventEmitter类来实现。EventEmitter是一个内置类位于events模块中通过继承EventEmitter你可以创建自己的事件发射器并注册和触发事件。通过这种机制Node.js 可以高效地处理异步任务即使在单线程的环境下也能实现并发处理。整个事件驱动的流程非常简洁有点类似于观察者模式事件相当于一个主题Subject而所有注册到这个事件上的处理函数相当于观察者Observer。基本概念概念说明事件在程序中发生的动作或状态改变例如一个文件读取完成或一个 HTTP 请求到达事件触发器EventEmitter是 Node.js 的内置模块用来发出和监听事件事件处理器与事件关联的回调函数事件发生时被调用事件驱动的流程注册事件在程序中通过EventEmitter实例注册事件和对应的处理器触发事件当指定的事件发生时EventEmitter会触发该事件处理事件事件循环会调度相应的回调函数来执行任务使用 EventEmitterNode.js 有多个内置的事件我们可以通过引入events模块并通过实例化EventEmitter类来绑定和监听事件// 引入 events 模块 var events require(events); // 创建 eventEmitter 对象 var eventEmitter new events.EventEmitter();绑定事件处理程序// 绑定事件及事件的处理程序 eventEmitter.on(eventName, eventHandler);触发事件// 触发事件 eventEmitter.emit(eventName);实例 1创建hello.js文件代码如下const EventEmitter require(events); const myEmitter new EventEmitter(); // 注册事件处理器 myEmitter.on(greet, () { console.log(Hello, world!); }); // 触发事件 myEmitter.emit(greet);输出Hello, world!实例 2创建main.js文件代码如下// 引入 events 模块 var events require(events); // 创建 eventEmitter 对象 var eventEmitter new events.EventEmitter(); // 创建事件处理程序 var connectHandler function connected() { console.log(连接成功。); // 触发 data_received 事件 eventEmitter.emit(data_received); } // 绑定 connection 事件处理程序 eventEmitter.on(connection, connectHandler); // 使用匿名函数绑定 data_received 事件 eventEmitter.on(data_received, function() { console.log(数据接收成功。); }); // 触发 connection 事件 eventEmitter.emit(connection); console.log(程序执行完毕。);执行以上代码$ node main.js 连接成功。 数据接收成功。 程序执行完毕。七、Node 应用程序是如何工作的在 Node 应用程序中执行异步操作的函数将回调函数作为最后一个参数回调函数接收错误对象作为第一个参数。创建一个input.txt文件内容如下地址www.chenbyte.cn创建main.js文件代码如下var fs require(fs); fs.readFile(input.txt, function(err, data) { if (err) { console.log(err.stack); return; } console.log(data.toString()); }); console.log(程序执行完毕);以上程序中fs.readFile()是异步函数用于读取文件。如果在读取文件过程中发生错误错误err对象就会输出错误信息。如果没发生错误readFile跳过err对象的输出文件内容就通过回调函数输出。执行以上代码执行结果如下程序执行完毕 地址www.chenbyte.cn接下来我们删除input.txt文件执行结果如下所示程序执行完毕 Error: ENOENT, open input.txt因为文件input.txt不存在所以输出了错误信息。八、本章小结知识点说明事件循环Node.js 处理非阻塞 I/O 的核心机制六个阶段Timers、I/O Callbacks、Idle/prepare、Poll、Check、Close Callbacks宏任务setTimeout、setInterval、setImmediate、I/O 操作微任务process.nextTick、Promise.then微任务优先执行process.nextTick优先级最高在当前操作结束后立即执行EventEmitter事件驱动编程的核心类用于注册和触发事件观察者模式事件相当于主题处理函数相当于观察者提示理解事件循环是掌握 Node.js 异步编程的关键。事件循环使得 Node.js 能够在单线程环境下高效处理大量并发 I/O 操作。记住微任务process.nextTick和Promise.then会在当前阶段结束后立即执行优先级高于宏任务。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门