查看“Node.js:事件循环、EventEmitter”的源代码
←
Node.js:事件循环、EventEmitter
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
[[category:Node.js]] == 关于 == Node.js 是'''单进程单线程'''应用程序,但是因为 V8 引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高。 * Node.js 几乎每一个 API 都是支持回调函数的。 * Node.js 基本上所有的事件机制都是用设计模式中'''观察者模式'''实现。 * Node.js 单线程类似进入一个 while(true) 的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数。 == 事件驱动程序 == Node.js 使用'''事件驱动模型''': # 当 web server 接收到请求,就把它关闭然后进行处理,然后去服务下一个 web 请求。 # 当这个请求完成,它被放回处理队列,当它到达队列开头,这个结果被返回给用户。 这个模型非常高效可扩展性非常强,因为 webserver '''一直接受请求而不等待任何读写操作'''。(这也称之为“'''非阻塞式I/O'''”或者“'''事件驱动I/O'''”) 在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。 : [[File:Node.js:事件驱动模型.png|400px]] 类似于观察者模式:事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数相当于观察者(Observer)。 Node.js 有多个内置的'''事件''',我们可以通过引入 '''events 模块''',并通过实例化 '''EventEmitter 类'''(事件触发器)来绑定和监听事件。 如下实例: # 引入“events”并实例化: #: <syntaxhighlight lang="bash" highlight=""> // 引入 events 模块 var events = require('events'); // 创建 eventEmitter 对象 var eventEmitter = new events.EventEmitter(); </syntaxhighlight> # 创建并绑定“事件处理程序”: #: <syntaxhighlight lang="bash" highlight=""> // 创建事件处理程序 var connectHandler = function xxx() { . . . } // 绑定事件及事件处理程序 eventEmitter.on('eventName', eventHandler); </syntaxhighlight> # 通过程序触发事件: #: <syntaxhighlight lang="bash" highlight=""> // 触发事件 eventEmitter.emit('eventName'); </syntaxhighlight> === 示例 === 创建 main.js 文件,代码如下所示: <syntaxhighlight lang="bash" highlight=""> // 引入 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("程序执行完毕。"); </syntaxhighlight> 执行以上代码: <syntaxhighlight lang="bash" highlight=""> $ node main.js 连接成功。 数据接收成功。 程序执行完毕。 </syntaxhighlight> == Node 应用程序是如何工作的? == 在 Node 应用程序中: # '''执行异步操作的函数将回调函数作为最后一个参数''', # '''回调函数接收错误对象作为第一个参数'''。 示例: # 创建一个 input.txt ,文件内容如下: #: <syntaxhighlight lang="bash" highlight=""> 菜鸟教程官网地址:www.runoob.com </syntaxhighlight> # 创建 main.js 文件,代码如下: #: <syntaxhighlight lang="bash" highlight=""> var fs = require("fs"); fs.readFile('input.txt', function (err, data) { if (err){ console.log(err.stack); return; } console.log(data.toString()); }); console.log("程序执行完毕"); </syntaxhighlight> 以上程序中 fs.readFile() 是异步函数用于读取文件: # 如果在读取文件过程中发生错误,错误 err 对象就会输出错误信息。 # 如果没发生错误,readFile 跳过 err 对象的输出,文件内容就通过回调函数输出。 执行以上代码,执行结果如下: <syntaxhighlight lang="bash" highlight=""> 程序执行完毕 菜鸟教程官网地址:www.runoob.com </syntaxhighlight> 接下来我们删除 input.txt 文件,执行结果如下所示: <syntaxhighlight lang="bash" highlight=""> 程序执行完毕 Error: ENOENT, open 'input.txt' </syntaxhighlight> 因为文件 input.txt 不存在,所以输出了错误信息。 == EventEmitter == '''Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列'''。 Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs.readStream 对象会在文件被打开的时候触发一个事件。 : 所有这些产生事件的对象都是 events.EventEmitter 的实例。 === EventEmitter 类 === events 模块只提供了一个对象: '''events.EventEmitter'''。 * EventEmitter 的核心就是'''事件触发'''与'''事件监听器功能'''的封装。 Note: # 可以通过 '''require("events");''' 来访问该模块: #: <syntaxhighlight lang="bash" highlight=""> // 引入 events 模块 var events = require('events'); // 创建 eventEmitter 对象 var eventEmitter = new events.EventEmitter(); </syntaxhighlight> #* EventEmitter 对象如果在实例化时发生错误,会触发 '''error''' 事件。 #* 当添加新的监听器时,'''newListener''' 事件会触发;当监听器被移除时,'''removeListener''' 事件被触发。 #: 示例:以下代码实现在 1 秒后控制台输出 “some_event 事件触发” #: <syntaxhighlight lang="bash" highlight=""> //event.js 文件 var EventEmitter = require('events').EventEmitter; var event = new EventEmitter(); event.on('some_event', function() { console.log('some_event 事件触发'); }); setTimeout(function() { event.emit('some_event'); }, 1000); </syntaxhighlight> #: <syntaxhighlight lang="bash" highlight=""> $ node event.js some_event 事件触发 </syntaxhighlight> # EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。 #* 对于每个事件,EventEmitter 支持若干个事件监听器:当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。 #: 示例:emitter 为事件 someEvent 注册了两个事件监听器,然后触发了 someEvent 事件 #: <syntaxhighlight lang="bash" highlight=""> //event.js 文件 var events = require('events'); var emitter = new events.EventEmitter(); emitter.on('someEvent', function(arg1, arg2) { console.log('listener1', arg1, arg2); }); emitter.on('someEvent', function(arg1, arg2) { console.log('listener2', arg1, arg2); }); emitter.emit('someEvent', 'arg1 参数', 'arg2 参数'); </syntaxhighlight> #: <syntaxhighlight lang="bash" highlight=""> $ node event.js listener1 arg1 参数 arg2 参数 listener2 arg1 参数 arg2 参数 </syntaxhighlight> ==== 方法 ==== EventEmitter 提供了多个方法,如: # '''on''':函数用于绑定事件函数 # '''emit''':属性用于触发一个事件。 {| class="wikitable" ! 方法 !! 描述 |- ! colspan="2" | 方法 |- | '''addListener(event, listener)''' | 为指定事件添加一个监听器到监听器数组的尾部。 |- | '''on(event, listener)''' | 为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。 <syntaxhighlight lang="bash" highlight=""> server.on('connection', function (stream) { console.log('someone connected!'); }); </syntaxhighlight> |- | '''once(event, listener)''' | 为指定事件注册一个单次监听器,即:监听器最多只会触发一次,触发后立刻解除该监听器。 <syntaxhighlight lang="bash" highlight=""> server.once('connection', function (stream) { console.log('Ah, we have our first user!'); }); </syntaxhighlight> |- | '''removeListener(event, listener)''' | 移除指定事件的某个监听器。 它接受两个参数,第一个是事件名称,第二个是回调函数名称: <syntaxhighlight lang="bash" highlight=""> var callback = function(stream) { console.log('someone connected!'); }; server.on('connection', callback); // ... server.removeListener('connection', callback); </syntaxhighlight> * 监听器必须是该事件已经注册过的监听器 |- | '''removeAllListeners([event])''' | 移除所有事件的所有监听器;如果指定事件,则移除指定事件的所有监听器。 |- | '''setMaxListeners(n)''' | 用于改变监听器的默认限制的数量。 * 默认情况下, EventEmitters 如果你添加的监听器超过 '''10''' 个就会输出警告信息。 |- | '''listeners(event)''' | 返回指定事件的监听器数组。 |- | '''emit(event, [arg1], [arg2], [...])''' | 按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 true,否则返回 false。 |- ! colspan="2" | 类方法 |- | listenerCount(emitter, event) | 返回指定事件的监听器数量。 <syntaxhighlight lang="bash" highlight=""> events.EventEmitter.listenerCount(emitter, eventName) // 已废弃,不推荐 events.emitter.listenerCount(eventName) // 推荐 </syntaxhighlight> |} ==== 事件 ==== {| class="wikitable" ! 事件 !! 描述 |- | newListener | 该事件在添加新监听器时被触发。 * event:字符串,事件名称 * listener:处理事件函数 |- | removeListener | 从指定监听器数组中删除一个监听器。需要注意的是,此操作将会改变处于被删监听器之后的那些监听器的索引。 * event:字符串,事件名称 * listener:处理事件函数 |} === 实例 === 以下实例通过 connection(连接)事件演示了 EventEmitter 类的应用: <syntaxhighlight lang="bash" highlight=""> var events = require('events'); var eventEmitter = new events.EventEmitter(); // 监听器 #1 var listener1 = function listener1() { console.log('监听器 listener1 执行。'); } // 监听器 #2 var listener2 = function listener2() { console.log('监听器 listener2 执行。'); } // 绑定 connection 事件,处理函数为 listener1 eventEmitter.addListener('connection', listener1); // 绑定 connection 事件,处理函数为 listener2 eventEmitter.on('connection', listener2); var eventListeners = eventEmitter.listenerCount('connection'); console.log(eventListeners + " 个监听器监听连接事件。"); // 处理 connection 事件 eventEmitter.emit('connection'); // 移除监绑定的 listener1 函数 eventEmitter.removeListener('connection', listener1); console.log("listener1 不再受监听。"); // 触发连接事件 eventEmitter.emit('connection'); eventListeners = eventEmitter.listenerCount('connection'); console.log(eventListeners + " 个监听器监听连接事件。"); console.log("程序执行完毕。"); </syntaxhighlight> 执行结果: <syntaxhighlight lang="bash" highlight=""> $ node main.js 2 个监听器监听连接事件。 监听器 listener1 执行。 监听器 listener2 执行。 listener1 不再受监听。 监听器 listener2 执行。 1 个监听器监听连接事件。 程序执行完毕。 </syntaxhighlight> === error 事件 === EventEmitter 定义了一个特殊的事件 '''error''',它包含了错误的语义,我们在遇到异常的时候通常会触发 error 事件。 * 当 error 被触发时,EventEmitter 规定如果没有响应的监听器,Node.js 会把它当作异常,退出程序并输出错误信息。 我们一般要为会触发 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。 例如: <syntaxhighlight lang="bash" highlight=""> var events = require('events'); var emitter = new events.EventEmitter(); emitter.emit('error'); </syntaxhighlight> 运行时会显示以下错误: <syntaxhighlight lang="bash" highlight=""> node.js:201 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: Uncaught, unspecified 'error' event. at EventEmitter.emit (events.js:50:15) at Object.<anonymous> (/home/byvoid/error.js:5:9) at Module._compile (module.js:441:26) at Object..js (module.js:459:10) at Module.load (module.js:348:31) at Function._load (module.js:308:12) at Array.0 (module.js:479:10) at EventEmitter._tickCallback (node.js:192:40) </syntaxhighlight> === 继承 EventEmitter === '''大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它'''。 * 包括 fs、net、 http 在内的,只要是'''支持事件响应的核心模块都是 EventEmitter 的子类'''。 为什么要这样做呢?原因有两点: # 首先,具有某个实体功能的对象实现事件符合语义,事件的监听和发生应该是一个对象的方法。 # 其次 JavaScript 的对象机制是基于原型的,支持'''部分多重继承'''【?】,继承 EventEmitter 不会打乱对象原有的继承关系。
返回至“
Node.js:事件循环、EventEmitter
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
大陆简体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
笔记
服务器
数据库
后端
前端
工具
《To do list》
日常
阅读
电影
摄影
其他
Software
Windows
WIKIOE
所有分类
所有页面
侧边栏
站点日志
工具
链入页面
相关更改
特殊页面
页面信息