1.简介
Node.js 事件循环是 Node.js 运行时环境中的一个核心机制,用于管理异步操作和回调函数的执行顺序。它基于事件驱动模型,通过事件循环来处理和派发事件,以及执行相应的回调函数。
- Node.js 是单进程单线程应用程序,但是因为 V8 引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高。
- Node.js 几乎每一个 API 都是支持回调函数的。
- Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。
- Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.
2.Node.js事件循环流程分析
Node.js 事件循环的主要步骤包括:
- 执行同步代码:首先,Node.js 会执行当前代码中的同步任务,以及它们所调用的同步函数。
- 执行异步代码:若有异步操作(例如 I/O 操作或网络请求),Node.js 会将其放入事件队列(Event Queue)中,然后继续执行后续的同步任务。
- 处理事件队列:当同步任务执行完毕或达到一个阈值时,Node.js 将开始处理事件队列。它会依序取出事件队列中的事件,执行相应的回调函数,并处理可能的新事件。
- 重复上述步骤:Node.js 会重复执行前述的步骤,直到事件队列为空。
对于每个事件的处理过程,Node.js 会执行以下操作:
- 执行回调函数:Node.js 会将事件从事件队列中取出,然后执行与该事件相关联的回调函数。
- 执行可能的同步操作:回调函数的执行过程中,可能会引发新的同步操作,Node.js 会立即执行这些同步操作。
- 返回控制权:回调函数的执行完毕后,Node.js 将返回控制权给事件循环,继续处理下一个事
总结:
Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。
当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。
这个模型非常高效可扩展性非常强,因为 webserver 一直接受请求而不等待任何读写操作。(这也称之为非阻塞式IO或者事件驱动IO)
在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。
整个事件驱动的流程就是这么实现的,非常简洁。有点类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数相当于观察者(Observer)。
3.时间循环举例说明
Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:
javascript
// 引入 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("程序执行完毕。");
执行结果:
shell
$ node test_event.js
连接成功。
数据接收成功。
程序执行完毕。
4.Node.js工作解析
当你运行一个 Node 应用程序时,它会经历以下步骤:
- 初始化:Node.js 在启动应用程序之前进行初始化操作,加载所需的模块和依赖项。
- 执行顶层代码:Node.js 开始执行应用程序的顶层代码,包括变量声明、函数定义等同步操作。
下面是一个简单的例子:
javascript
const http = require('http');
function onRequest(request, response) {
response.writeHead(200, { 'Content-Type': 'text/plain' });
response.write('Hello, World!');
response.end();
}
const server = http.createServer(onRequest);
server.listen(3000);
console.log('Server is running on port 3000');
在这个例子中,我们通过 http 模块创建了一个 HTTP 服务器,并将其绑定到 3000 端口。当服务器收到请求时,会调用 onRequest 函数处理请求并返回响应。
- 异步操作和回调函数:Node.js 在执行顶层代码后,处理应用程序中的异步操作。这包括处理网络请求、文件读写、数据库查询等。举个例子,假设我们的应用程序要读取一个文件并将其中的内容打印到控制台:
javascript
const fs = require('fs');
fs.readFile('data.txt', 'utf8', function (error, data) {
if (error) {
console.error('Error:', error);
return;
}
console.log('File content:', data);
});
在这个例子中,我们使用 fs 模块异步地读取名为 data.txt 的文件。读取操作完成后,调用回调函数来处理读取的文件内容。
- 事件循环:Node.js 的事件循环在后台运行,不断地检查事件队列,并执行相应的回调函数。
如果我们将上述的 HTTP 服务器和文件读取的例子结合起来:
javascript
const http = require('http');
const fs = require('fs');
function onRequest(request, response) {
fs.readFile('data.txt', 'utf8', function (error, data) {
if (error) {
response.writeHead(500, { 'Content-Type': 'text/plain' });
response.write('Internal Server Error');
response.end();
return;
}
response.writeHead(200, { 'Content-Type': 'text/plain' });
response.write(data);
response.end();
});
}
const server = http.createServer(onRequest);
server.listen(3000);
console.log('Server is running on port 3000');
现在,当收到 HTTP 请求时,服务器会异步地读取文件,并在读取完成后发送文件内容作为响应。
- 关闭应用程序:当应用程序终止或手动关闭时,Node.js 会执行一些清理操作并释放资源。
上述例子中的应用程序可以通过按下 Ctrl+C 组合键来关闭。
Node.js 的运行方式使它适用于处理高并发和实时应用程序,因为它能够以非阻塞的方式处理异步操作,提供高性能和可伸缩性。