爱妃科技nodejs Event API | | 爱妃科技
正在加载
请稍等

菜单

红楼飞雪 梦

15526773247

文章

Home nodejs nodejs Event API
Home nodejs nodejs Event API

nodejs Event API

nodejs by

Node 提供了许多API,其中一些比较重要。这些核心API 是所有Node 应用的支柱,你会不停地用到它们。

我们第一个要研 究的API 是Events API。这是因为,尽管抽象,但它是其他所有API 工作的基础模块。通过仔细查看这个API,你就能很好地使用其他所有的API。如果你曾经在浏览器里 开发过JavaScript 程序,就一定已经使用Events 了。但是,浏览器的事件模型是从DOM 里来的,不是JavaScript 本身自带的。DOM 的许多理念在其使用情景之外并没有太多用处。我们先看看DOM 模型的Events,然后再与Node 的实现方式进行比较。DOM 是基于用户交互的用户驱动型事件模型, 有着一组与树状结构(HTML,XML,等等)对应的接口元素。意思是,当用户与接口的某个特定部分交互时,对应有一个事件和一个相关的对象,比如某个 HTML/XML 元素被点击或者进行了其他操作。该操作对象有父节点,并且可能有子节点。 因为操作对象是在一棵树中,所以这个模型包含了冒泡和捕获的概念,就是允许沿着树 的结构向上或向下的元素也接收被触发的事件。例如,在一个HTML 列表中,在<li> 上的一个点击事件,能够被其父节点<ul>上绑定的监听器所捕获。反过来,<ul> 上的点击会向下冒泡传给<li> 上的监听器。因为JavaScript 对象没有这一类树状结构,所以Node 中的模型更加简单。

EventEmitter

因 为在浏览器中Event 模型是绑定在DOM 上的,所以Node 创建了EventEmitter类来提供基础的事件功能。所有Node 的事件功能围绕着EventEmitter,因为它的设计包含了其他类扩展所需要的接口类。EventEmitter 对象通常不会直接调用。EventEmitter 类提供了一系列方法,其中最主要的两个是on 和emit,这些方法供其他类使用。on 方法为一个事件创建了监听器,

使用on 方法监听事件

server.on('event', function (a, b, c) {
  // 具体操作
});

on 方法接受两个参数:需要监听的事件的名称,当事件触发时需要调用的函数。因为EventEmitter 是接口,从EventEmitter 继承的类需要使用new 关键字来构造。让我们看看例4-2 中如何构造一个监听器的新类。

创建一个新类支持EventEmitter 事件

var utils = require('utils'),
EventEmitter = require('events').EventEmitter;
var Server = function () {
  console.log('init');
};
utils.inherits(Server, EventEmitter);
var s = new Server();
s.on('abc', function () {
  console.log('abc');
});

例子中, 我们先包含了utils 模块, 以便调用它的inherits 方法。inherits能够把EventEmitter 类的方法添加到我们创建的Server 类中。这意味着所有Server 的新实例都能够使用EventEmitter 的方法。然后我们包含了events 模块。但我们只想调用其模块中的EventEmitter 类。注意,EventEmitter 是以大写字母开始命名的,用来表示它本身是一个类。我们不调用createEventEmitter 方法,因为不打算直接使用EventEmitter 实例,只是想把它的方法绑定到我们要用的Server 类上。当包含了所有需要的模块后,下一步就是创建基础的Server 类。它只提供了一个简单的函数,就是在初始化的时候记录一条消息。在真实的实现中,我们会为Server 类补充它需要使用的函数原型。为了简单起见,我们先把这部分省略了。重要的一步是使用sys.inherits 把EventEmitter 作为超类添加给Server 类。当需要使用Server 类时,我们用new Server() 来实例化它。Server 的实例能够访问其超类(EventEmitter)的方法,也就是说我们可以调用on 方法来为这个实例添加监听器。到目前为止,我们添加的事件监听器还不会被调用,因为并没有abc 事件被触发。我们可以通过添加如下所示的代码来触发这个事件。

触发一个事件

s.emit('abc');

触 发事件监听器很简单,只要调用从EventEmitter 继承的Server 实例的emit方法就行了。需要注意的是,这些事件是针对某个实例的,不存在全局的事件。当你调用on 方法的时候,需要绑定在特定的基于EventEmitter 的对象上。Server类不同的实例之间也不会共享事件。上述代码中的s 对象不会与另外一个Server实例z(比如var z = new Server())共享同一个事件。

Callback语法

使 用事件很重要的一个部分是处理回调函数。第3 章已经深入地探讨了其最佳实践,但我们现在要看看在Node 里回调函数的工作机制。它们采用了几种标准模式,我们先来看看有哪些可能性。当调用emit 时,除了事件的名称,你可以传入任意数目的参数。下面例子中包含了3个这样的参数。这些参数都将传给该监听该事件的函数。比如,从http 服务器接收到request 请求时,你会收到两个参数:req 和res。当request 事件被触发时,这些参数会作为第二个和第三个参数传给emit 函数。

触发事件的时候传递参数

s.emit('abc', a, b, c);

了解Node 如何调用事件监听器很重要,因为这会影响你的编程风格。当emit() 调用包含变量时,下例中的代码会被用来调用对应的事件监听器。

触发器里如何调用事件

if (arguments.length <= 3) {
  // 速度快
  handler.call(this, arguments[1], arguments[2]);
} else {
  // 速度慢
  var args = Array.prototype.slice.call(arguments, 1);
  handler.apply(this, args);
}

这 两种代码都是JavaScript 调用函数的方法。如果传给emit() 的参数只有3 个或更少, 该方法就会使用捷径, 直接调用call 方法。否则, 它就会使用较慢的apply 方法,以数组的方式传递所有的参数。这里需要注意的是,Node 调用这两种方法时都直接使用了this 参数。这意味着事件监听器被调用的时候是在EventEmitter 的上下文中,而不是它们原始的位置。通过Node 命令行解析器,你可以清楚地看见当EventEmitter 调用对象时会发生什么事情

EventEmitter 改变了上下文

> var EventEmitter = require('events').EventEmitter,
... util = require('util');
>
> var Server = function() {};
> util.inherits(Server, EventEmitter);
> Server.prototype.outputThis= function(output) {
... console.log(this);
... console.log(output);
... };
[Function]
>
> Server.prototype.emitOutput = function(input) {
... this.emit('output', input);
... };
[Function]
>
> Server.prototype.callEmitOutput = function() {
... this.emitOutput('innerEmitOutput');
... };
[Function]
>
> var s = new Server();
> s.on('output', s.outputThis);
{ _events: { output: [Function] } }
> s.emitOutput('outerEmitOutput');
{ _events: { output: [Function] } }
outerEmitOutput
> s.callEmitOutput();
{ _events: { output: [Function] } }
innerEmitOutput
> s.emit('output', 'Direct');
{ _events: { output: [Function] } }
Direct
true
>

输出例子中首先设置了一个 Server 类, 它包含了触发output 事件的函数。outputThis 方法作为事件监听器绑定在output 事件上。在不同的上下文中触发output 事件时, 我们保持在EventEmitter 对象所在的作用域中。所以s.outputThis 能够访问的this 变量的值是属于该EventEmitter 的。因此,如果我们想在事件回调函数中使用this 变量的话,就必须把它作为一个参数传入并赋值到另外一个变量上。

 

23 2015-06

 

我要 分享

 

 

本文 作者

 

相关 文章