node的事件模块应用(译)
第一次接触Node.js时,就觉得他只不过是用javascript实现的服务端。但实际上他提供了许多浏览器端不具备的方法,比如EventEmitter类。我们在本文中来学习如何使用EventEmitter。
EventEmitter是什么?
简单来说,使用EventEmitter,你可以监听一个事件,并且可以执行一个你绑定的回调函数。就像前端的javascript一样,你可以通过addEventListener来绑定用户的鼠标键盘交互事件,EventEmitter是基于发布订阅模式,因此我们可以通过订阅事件然后再发布。我们可以看到很多前端javascript库是支持订阅发布模式,但Node.js是内建的。
有一个重要的问题:你为什么要使用事件模式?因为在Node.js里,他可以替代各种深层嵌套的加调。Node.js很多方法是要异步运行的,这意味着当这个方法完成时,你就要传递一个可回调的函数。最后,你会发现你的代码看起来像一个巨大的漏斗。为了防止这种情况出现,你可以使用监听事件来优化这些事件,这可以更好地组织你的代码,而不是使用回调嵌套的方式。
使用事件方式还有一个好处,就是可以使你的代码得到很好的解耦。一个事件发出后,如果没有被监听,那么他也不会报错。
使用 EventEmitter
现在让我们一起开始使用EventEmitter,首先引用events模块:
var events = require("events");
这个events对象有一个属性,就是EventEmitter类本身,现在我们做一个简单的例子来学习他:
var EventEmitter = require("events").EventEmitter;
var ee = new EventEmitter();
ee.on("someEvent", function () {
console.log("event has occured");
});
ee.emit("someEvent");
首先我们创建一个新的EventEmitter对象。这个对象有两种主要方法: on 和 emit; on这个方法有两个参数,第一个参数是我们要监听事件的名称,在上面的例子,要监听事件的名称就是"someEvent"。当然你可以定一个更好的名字。第二个参数是在事件发生时,要执行的函数。
现在要触发事件,你可以通过事件名称EventEmitter的实例emit方法。就是上面代码的最后一行。如果你运行代码,将得到打印到控制台的文本。
这是最基本的 EventEmitter 使用,你也可以触发事件时传递一个对象。
ee.emit("new-user", userObj);
这只是一个数据参数,可以包含你想要的数据。可以在事件函数中可以通过参数来接收他:
ee.on("new-user", function (data) {
// use data here
});
让我们理解清楚EventEmitter功能的一部分。其实一个事件不止被监听一次,还可一个事件被监听多次,并且当事件被触发时,所有监听者的事件都会被触发。默认情况下,Node.js允许一个事件同时被监听10次。如果再创建Node.js会发出警告。但是,我们可以通过使用setMaxListeners改变这个限制数。
例如,如果你运行下面的代码,你应该会看到警告信息。
ee.on("someEvent", function () { console.log("event 1"); });
ee.on("someEvent", function () { console.log("event 2"); });
ee.on("someEvent", function () { console.log("event 3"); });
ee.on("someEvent", function () { console.log("event 4"); });
ee.on("someEvent", function () { console.log("event 5"); });
ee.on("someEvent", function () { console.log("event 6"); });
ee.on("someEvent", function () { console.log("event 7"); });
ee.on("someEvent", function () { console.log("event 8"); });
ee.on("someEvent", function () { console.log("event 9"); });
ee.on("someEvent", function () { console.log("event 10"); });
ee.on("someEvent", function () { console.log("event 11"); });
ee.emit("someEvent");
为了可以设置更大的监听数量,可以运行下面的代码:
ee.setMaxListeners(20);
当你运行他,你将不会再得到警告。
其他 EventEmitter 方法
还有其他非常有用的 EventEmitter方法 once, 像on方法,不同的是它仅适用于一次,被触发一次后,监听者会被删除。
ee.once("firstConnection", function () {
console.log("You'll never see this again");
});
ee.emit("firstConnection");
ee.emit("firstConnection");
如果你运行上面的代码,你只能看到一次消息。第二次emit是不会触发任何事件,因为once监听者被使用后,就会被删除。
现在提到了去除监听者,其实我们也可以手动做到这点,有几个办法. 第一:我们可以通过removeListener方法去删除单个监听者,他需要两个参数:事件名称和监听器函数。到目前为止,我们一直在使用匿名函数作为我们的听众。如果我们希望以后可以删除监听器,那需要一个非匿名函数。我们可以参考一下removeListener方法。
function onlyOnce () {
console.log("You'll never see this again");
ee.removeListener("firstConnection", onlyOnce);
}
ee.on("firstConnection", onlyOnce)
ee.emit("firstConnection");
ee.emit("firstConnection");
如果你运行上面的代码,你会看到和 once 方法非常相似的效果。
如果你想删除所有的监听者,你可以使用removeAllListeners,只需要给他传递一个事件的名称。
ee.removeAllListeners("firstConnection");
如果想删除所有监听事件,调用函数不用带任何参数。
ee.removeAllListeners();
还有最后一个方法:listeners.这个方法接受一个事件称作为参数,并返回了监听事件的函数数组。看例子:
function onlyOnce () {
console.log(ee.listeners("firstConnection"));
ee.removeListener("firstConnection", onlyOnce);
console.log(ee.listeners("firstConnection"));
}
ee.on("firstConnection", onlyOnce)
ee.emit("firstConnection");
ee.emit("firstConnection");
EventEmitter实例本身触发了两个事件,我们可以同样监听到:一个是我们新创建的监听者时触 发的事件newListener,另一个是我们删除监听者后触发的事件removeListener。看下面代码:
ee.on("newListener", function (evtName, fn) {
console.log("New Listener: " + evtName);
});
ee.on("removeListener", function (evtName) {
console.log("Removed Listener: " + evtName);
});
function foo () {}
ee.on("save-user", foo);
ee.removeListener("save-user", foo);
运行上线代码,每当前建监听者和删除监听者,你都会看到我们的预期消息。
现在我们已经看到EventEmitter实例的所有方法,让我们来看看它是如何工作与其他模块一起使用。
EventEmitter内部模块
由于EventEmitter类只是普通的javascript,它非常有意义,它可以在其实模块中使用,在你的javascript模块,你可以创建EventEmitter实例,并用它们来处理内部事件。这很简单,更有趣的是,是创建继承自EventEmitter一个模块,所以我们可以使用公共API实现部分功能。
其实,有内置的Node模块做的正是这一点。例如,HTTP模块,这是用来创建web server的一个模块。下面的示例是展示EventEmitter类的on方法如何成为http.Server类的一部分。
var http = require("http");
var server = http.createServer();
server.on("request", function (req, res) {
res.end("this is the response");
});
server.listen(3000);
如果你运行这段代码,进程将等待一个请求,你可以去访问http://localhost:3000,你会得到响应。当服务器实例从浏览器获取请求时,它会发出一个“请求”事件,我们的监听器将接收并在可以充当一个事件。
那么,我们如何去创造一个继承于EventEmitter的类?这其实并不难。我们将创建一个简单的UserList类,它负责处理用户对象。所以,在userlist.js文件中:
var util = require("util");
var EventEmitter = require("events").EventEmitter;
我们需要的util模块,以帮助我们实现继承。接下来,我们需要一个数据库:而不是使用一个真正的数据库,他只是一个对象:
var id = 1;
var database = {
users: [
{ id: id++, name: "Joe Smith", occupation: "developer" },
{ id: id++, name: "Jane Doe", occupation: "data analyst" },
{ id: id++, name: "John Henry", occupation: "designer" }
]
};
现在,我们实可以创造我们的模块。如果你不熟悉Node.js模块,这简单介绍他们是如何工作的:这个文件里面的任何JavaScript是只可读的,默认情况下。如果我们想让它模块的公共API的一部分,我们把它module.exports的属性或module.exports,指定一个全新的对象或函数。让我们这样做:
function UserList () {
EventEmitter.call(this);
}
这是构造函数,但它不是你平常的JavaScript构造函数。我们在EventEmitter的构造函数上使用call方法作用于UserList对象。继承构造函数是不够的,我们还需要继承他的原型。这时需要使用到util模块的继承功能了。
util.inherits(UserList, EventEmitter);
这就可以把EventEmitter.prototype上的方法添加到 UserList.prototype 了,我们的UserList实例拥有了EventEmitter实例的所有方法,但我们想添加更多方法,可以添加一个save方法去增加新用户。
UserList.prototype.save = function (obj) {
obj.id = id++;
database.users.push(obj);
this.emit("saved-user", obj);
};
此方法可以保存一个对象到我们的“数据库”:它增加了一个id,并将其加入到用户数组。然后,它发出的“saved-user”事件,并且对象传递数据。如果这是一个真正的数据库,保存它很可能是一个异步的任务,这意味着与保存的记录,我们就必须接受一个回调的工作。
另一种情况是发出一个事件,如果我们想做保存的记录的工作,我们就可以监听其事件。我们接下来处理这些事情。
UserList.prototype.all = function () {
return database.users;
};
module.exports = UserList;
我增加了一个方法:一个简单的返回所有的用户,然后,我们指定的UserList到module.exports。 现在,让我们来尝试使用他,在另一个文件中,test.js添加以下内容:
var UserList = require("./userlist");
var users = new UserList();
users.on("saved-user", function (user) {
console.log("saved: " + user.name + " (" + user.id + ")");
});
users.save({ name: "Jane Doe", occupation: "manager" });
users.save({ name: "John Jacob", occupation: "developer" });
我们引入刚设计好的模块,并创建它的一个实例后,我们监听了”saved-user”事件,然后,我们可以继续保存用户信息,当我们运行后,就可以看到,得到两个信息,打印出我们保存的记录名字和ID:
saved: Jane Doe (4)
saved: John Jacob (5)
结论
Node.js的EventEmitter类是如何工作的呢。大家可以从下面的文档链接了解到更多信息。
Node Events Module Node Util Module Node HTTP Agent Source – This shows the inheritance pattern we used.
本文译自:http://net.tutsplus.com/tutorials/javascript-ajax/using-nodes-event-module/
本文早期由我发到前端乱炖,现在转过来到个人博客。
node的事件模块应用(译)的更多相关文章
- nodejs -- event 模块, 事件模块.
1. 注册事件 on 或者 addListener,触发事件 emit 1-1简单的使用: var EventEmitter = require('events').EventEmitter; var ...
- Node.js事件的正确使用方法
前言 事件驱动的编程变得流行之前,在程序内部进行通信的标准方法非常简单:如果一个组件想要向另外一个发送消息,只是显式地调用了那个组件上的方法.但是在 react 中用的却是事件驱动而不是调用. 事件的 ...
- 自己实现一个javascript事件模块
nodejs中的事件模块 nodejs中有一个events模块,用来给别的函数对象提供绑定事件.触发事件的能力.这个别的函数的对象,我把它叫做事件宿主对象(非权威叫法),其原理是把宿主函数的原型链指向 ...
- Node.js 事件
Node.js 事件 Node.js 所有的异步I/O 操作在完成时都会发送一个事件到事件队列. Node.js里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时分发一个事件, ...
- Node.js 事件循环(Event Loop)介绍
Node.js 事件循环(Event Loop)介绍 JavaScript是一种单线程运行但又绝不会阻塞的语言,其实现非阻塞的关键是“事件循环”和“回调机制”.Node.js在JavaScript的基 ...
- Node.js事件循环
Node JS是单线程应用程序,但它通过事件和回调概念,支持并发. 由于Node JS每一个API是异步的,作为一个单独的线程,它使用异步函数调用,以保持并发性.Node JS使用观察者模式.Node ...
- node.js零基础详细教程(4):node.js事件机制、node异步IO操作
第四章 建议学习时间3小时 课程共10章 学习方式:详细阅读,并手动实现相关代码 学习目标:此教程将教会大家 安装Node.搭建服务器.express.mysql.mongodb.编写后台业务逻辑. ...
- Node.js Net 模块
Node.js Net 模块提供了一些用于底层的网络通信的小工具,包含了创建服务器/客户端的方法,我们可以通过以下方式引入该模块: var net = require("net") ...
- Node.js 事件循环机制
Node.js 采用事件驱动和异步 I/O 的方式,实现了一个单线程.高并发的 JavaScript 运行时环境,而单线程就意味着同一时间只能做一件事,那么 Node.js 如何通过单线程来实现高并发 ...
随机推荐
- LinQ高级查询
1.模糊查询 con.Users.Where(a =>a.UserName.Contains(name)).ToList(); //包含name con.Users.Where(a =>a ...
- PPTP-VPN第一章——部署与简单使用
最近前游戏同事搞了台的VPS,贡献出来做VPN使用.目前流行的服务器VPN软件主要有PPTP VPN和Openvpn.Openvpn虽然功能较为强大,且安全性高,支持nat穿越等等,但服务器端和客户端 ...
- [知识整理]Java集合(一) - List
一.实现List的几个类: ArrayList.LinkedList.CopyOnWriteArrayList.Vector 二.几个List底层的数据结构: ArrayList - 数组列表 Lin ...
- 关于NIO
操作系统的IO控制 在整个IO控制方式的发展过程中,始终贯穿着这样一条宗旨:即尽量减少主机对IO控制的干预,把主机从繁杂的IO控制事务中解脱出来,以便更多地去完成数据处理任务.为了缓和高速CPU和IO ...
- 3.3.2 pulseIn(pin,state,timeout)
pulseIn函数用于读取引脚脉冲的时间长度,脉冲可以是HIGH或LOW.如果是HIGH,函数将先等引脚变为高电平,然后开始计时,一直到变为低电平为止.返回脉冲持续的时间长短, 单位为ms.如果超时还 ...
- NGUI 屏幕自适应(初始设定宽高800x480只支持比其大的屏幕)
自适应讲解部分可以参考以下网址:http://www.xuanyusong.com/archives/2536,下面代码中提到的AdaptiveManualHeight()函数就是参考该文章的. 下面 ...
- JS-数组冒泡排序
数组冒泡就是每次从数组里取出两个数,然后比较大小,然后在排序. 正序排列 var arr = [8,2,5,12,14,19,1,6]; for(var i = 0;i < arr.l ...
- jQuery Mobile 列表内容
jQuery Mobile 列表缩略图 对于大于 16x16px 的图像,请在链接中添加 <img> 元素. jQuery Mobile 将自动把图像调整至 80x80px: 实例: &l ...
- php开发环境搭建——laravel框架,apache服务器,git版本控制
本文主要阐述做项目前的开发环境安装——后端为php,前端采用grunt进行自动化构建.具体介绍了windows平台下采用apache运行php的环境搭建,以及git工具安装.写得有点粗糙,但过程完整, ...
- 记录第一次搭建svn服务器
搭建svn服务器需要另外的软件, 在此以32位的为例: 安装过程非常简单, 一直下一步下一步确定就好了, svn安装完毕再安装中文语言包, 安装完成后可以在设置里面找到中文简体选择就OK了 主要记录一 ...