跳到主要内容

08、Node.js 事件循环

Node.js 是单进程单线程异步 IO 应用程序,通过事件和回调支持并发

Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发

Node.js 几乎所有的事件都使用观察者模式实现 (观察者模式是设计模式中的一种)

Node.js 每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数

事件驱动模式

Node.js 使用事件驱动模式,当 Node.js WEB Server 收到请求,就把它放到待处理队列里然后去服务下一个请求

等到请求完成, 请求就会放回到处理完毕队列里,当到达队列开头,这个结果被返回给用户

这种异步模型非常高效可扩展性非常强,因为 WEB Server 一直接受请求而不等待任何读写操作

在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数

 

整个事件驱动的流程就是这么实现的,非常简洁,有点类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数相当于观察者(Observer)

Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件

引入事件模块

var events = require('events');

生成 events 对象

// 引入 events 模块
var events = require('events');

// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();

绑定事件处理函数

// 绑定事件及事件的处理程序
eventEmitter.on('eventName', eventHandler);

触发事件

可以使用 events.emit() 方法来触发一个事件

// 触发事件
eventEmitter.emit('eventName');

范例

接下来我们来创建一个事件,然后触发这个事件,也就是把上面提到的流程组合起来

main.js

/*
 * filename: main.js
 * author: DDKK.COM 弟弟快看,程序员编程资料站(ddkk.com)
 * Copyright © 2015-2065 ddkk.com. All rights reserved.
*/

// 引入 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.js 范例,输出结果如下

$ node main.js
连接成功。
数据接收成功。
程序执行完毕

Node.js 应用程序是如何工作的?

Node.js 应用程序,执行异步操作的函数将回调函数作为最后一个参数, 回调函数接收错误对象作为第一个参数

我们使用一个范例来解释这种机制

假设当前目录下存在 demo.txt 文件,内容如下

$ cat demo.txt 
DDKK.COM 弟弟快看,程序员编程资料站,教程 
DDKK.COM 弟弟快看,程序员编程资料站官网 : https://ddkk.com

然后我们使用 fs.readFile 方法异步读取这个文件并显示结果

main.js

/*
 * filename: main.js
 * author: DDKK.COM 弟弟快看,程序员编程资料站(ddkk.com)
 * Copyright © 2015-2065 ddkk.com. All rights reserved.
*/

var fs = require("fs");

fs.readFile('demo.txt', function (err, data) {
   if (err){
      console.log(err.stack);
      return;
   }
   console.log(data.toString());
});
console.log("程序执行完毕");

这个范例中,fs.readFile() 用于异步读取文件 如果在读取文件过程中发生错误,错误 err 对象就会输出错误信息

如果没发生错误,readFile 跳过 err 对象的输出,文件内容就通过回调函数输出

运行以上 Node.js 脚本,输出结果如下

$ node main.js 
程序执行完毕
DDKK.COM 弟弟快看,程序员编程资料站,教程 
DDKK.COM 弟弟快看,程序员编程资料站官网 : https://ddkk.com

如果我们删除了 demo.txt 则,输出结果如下:

$ node main.js
程序执行完毕
Error: ENOENT: no such file or directory, open 'demo.txt'

因为文件 demo.txt 不存在,所以输出了错误信息