求助:node.jsnodejs 自定义error模块加载后emit('error'...未执行

OurJS-我们的JS, 我们的技术-IT文摘; 专注JS相关领域;
我们热爱编程, 我们热爱技术;我们是高大上, 有品味的码农;
欢迎您订阅我们的技术周刊
我们会向您分享我们精心收集整理的,最新的行业资讯,技术动态,外文翻译,热点文章;
我们使用第三方邮件列表向您推送,我们不保存您的任何个人资料,注重您的隐私,您可以随时退订,
欢迎分享您的观点,经验,技巧,心得
让我们一起找寻程序员的快乐,探索技术, 发现IT人生的乐趣;
本网站使用缓存技术每次加载仅需很小流量, 可在手机中流畅浏览;
如果您发现任何BUG,请即时告知我们: ourjs(at)ourjs.com
订阅邮件周刊
拿什么守护你的Node.JS进程: Node出错崩溃了怎么办?
注意 转载须保留原文链接,译文链接,作者译者等信息。&&
被吐嘈的NodeJS的异常处理许多人都有这样一种映像,NodeJS比较快; 但是因为其是单线程,所以它不稳定,有点不安全,不适合处理复杂业务; 它比较适合对并发要求比较高,而且简单的业务场景。在Express的作者的TJ Holowaychuk的一文中列举了以下罪状:Farewell NodeJS (TJ Holowaychuk)o&& you may get duplicate callbackso&& you may not get a callback at all (lost in limbo)o&& you may get out-of-band errorso&& emitters may get multiple “error” eventso&& missing “error” events sends everything to hello&& often unsure what requires “error” handlerso&& “error” handlers are very verboseo&& callbacks suck其实这几条主要吐嘈了两点: node.js错误处理很扯蛋,node.js的回调也很扯蛋。事实上呢?事实上NodeJS里程确实有“脆弱”的一面,单线程的某处产生了“未处理的”异常确实会导致整个Node.JS的崩溃退出,来看个例子, 这里有一个node-error.js的文件:var http = require('http');var server = http.createServer(function (req, res) {
//这里有个错误,params 是 undefined
var ok = req.params.
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');});server.listen(8080, '127.0.0.1');console.log('Server running at http://127.0.0.1:8080/');启动服务,并在地址栏测试一下发现 & 不出所料,node崩溃了$ node node-errorServer running at http://127.0.0.1:8080/c:\github\script\node-error.js:5
var ok = req.params.
^TypeError: Cannot read property 'ok' of undefined
at Server.&anonymous& (c:\github\script\node-error.js:5:22)
at Server.EventEmitter.emit (events.js:98:17)
at HTTPParser.parser.onIncoming (http.js:2108:12)
at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:121:23)
at Socket.socket.ondata (http.js:1966:22)
at TCP.onread (net.js:525:27)怎么解决呢?其实Node.JS发展到今天,如果连这个问题都解决不了,那估计早就没人用了。使用uncaughtException我们可以uncaughtException来全局捕获未捕获的Error,同时你还可以将此函数的调用栈打印出来,捕获之后可以有效防止node进程退出,如:process.on('uncaughtException', function (err) {
//打印出错误
console.log(err);
//打印出错误的调用栈方便调试
console.log(err.stack);});这相当于在node进程内部进行守护, 但这种方法很多人都是不提倡的,说明你还不能完全掌控Node.JS的异常。使用 try/catch我们还可以在回调前加try/catch,同样确保线程的安全。var http = require('http');http.createServer(function(req, res) {
handler(req, res);
} catch(e) {
console.log('\r\n', e, '\r\n', e.stack);
res.end(e.stack);
} catch(e) { }
}}).listen(8080, '127.0.0.1');console.log('Server running at http://127.0.0.1:8080/');var handler = function (req, res) {
//Error Popuped
var name = req.params.
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello ' + name);};这种方案的好处是,可以将错误和调用栈直接输出到当前发生的网页上。集成到框架中标准的HTTP响应处理会经历一系列的Middleware(HttpModule),最终到达Handler,如下图所示:这些Middleware和Handler在NodeJS中都有一个特点,他们都是回调函数,而回调函数中是唯一会让Node在运行时崩溃的地方。根据这个特点,我们只需要在框架中集成一处try/catch就可以相对完美地解决异常问题,而且不会影响其它用户的请求request。事实上现在的NodeJS WEB框架几乎都是这么做的,如所基于的就有这么一处异常处理代码:Line: 207& try {&&& handler(req, res);& } catch(err) {&&& var errorMsg&&&&& = '\n'&&&&& + 'Error ' + new Date().toISOString() + ' ' + req.url&&&&& + '\n'&&&&& + err.stack || err.message || 'unknow error'&&&&& + '\n'&&&&& ;&&& console.error(errorMsg);&&& Settings.showError&&&&& ? res.end('&pre&' + errorMsg + '&/pre&')&&&&& : res.end();& }那么不在回调中产生的错误怎么办?不必担心,其实这样的node程序根本就起不起来。此外node自带的
也有一定的容错能力,它跟nginx的worker很类似,但消耗资源(内存)略大,编程也不是很方便,OurJS并没有采用此种设计,未来如果流量达到一定程度,单个进程无法满足要求时,或采用多个服务器(VMs),起多个相互独立的node&websvr进程,将需要共享的session存放在一处统一的redis数据库中。守护NodeJS进程和记录错误日志现在已经基本上解决了Node.JS因异常而崩溃的问题,不过任何平台都不是100%可靠的,还有一些错误是从Node底层抛出的,有些异常try/catch和uncaughtException都无法捕获。之前在运行ourjs的时侯,会偶尔碰到底层抛出的文件流读取异常,这就是一个底层libuv的BUG,node.js在0.10.21中进行了修复。面对这种情况,我们就应该为nodejs应用添加守护进程,让NodeJS遭遇异常崩溃以后能马上复活。另外,还应该把这些产生的异常记录到日志中,并让异常永远不再发生。使用node来守护node 提供了守护的功能和LOG日志记录功能。安装非常容易[sudo] npm install forever使用也很简单$ forever start simple-server.js$ forever list& [0] simple-server.js [ 2 ]还可以看日志forever -o out.log -e err.log my-script.js使用shell启动脚本守护node使用node来守护的话资源开销可能会有点大,而且也会略显复杂,OurJS直接在开机启动脚本来进程线程守护。如在debian中放置的 ourjs 开机启动文件: 这个文件非常简单,只有启动的选项,守护的核心功能是由一个无限循环 来实现的,为了防止过于密集的错误阻塞进程,每次错误后间隔1秒重启服务WEB_DIR='/var/www/ourjs'WEB_APP='svr/ourjs.js'#location of node you want to useNODE_EXE=/root/local/bin/node do&&& {&&&&&&& $NODE_EXE $WEB_DIR/$WEB_APP config.magazine.js&&&&&&& echo "Stopped unexpected, restarting \r\n\r\n"&&& } 2&& $WEB_DIR/error.log&&& sleep 1done错误日志记录也非常简单,直接将此进程控制台当中的错误输出到error.log文件即可: 2&& $WEB_DIR/error.log& 这一行, 2 代表 Error。
&热门文章 - 分享最多
&相关阅读 - JS学习
&关键字 - OurJS
&欢迎订阅 - 技术周刊
我们热爱编程, 我们热爱技术; 我们是高端, 大气, 上档次, 有品味, 时刻需要和国际接轨的码农; 欢迎您订阅我们的技术周刊; 您只需要在右上角输入您的邮箱即可; 我们注重您的隐私,您可以随时退订.
加入我们吧! 让我们一起找寻码农的快乐,探索技术, 发现IT人生的乐趣;
我们的微信公众号: ourjs-com
打开微信扫一扫即可关注我们:
IT文摘-程序员(码农)技术周刊登录以解锁更多InfoQ新功能
获取更新并接收通知
给您喜爱的内容点赞
关注您喜爱的编辑与同行
966,690 四月 独立访问用户
语言 & 开发
架构 & 设计
文化 & 方法
您目前处于:
深入浅出Node.js(四):Node.js的事件机制
深入浅出Node.js(四):Node.js的事件机制
14&他的粉丝
日. 估计阅读时间:
,PWA、Web框架、Node等最新最热的大前端话题邀你一起共同探讨。
亲爱的读者:我们最近添加了一些个人消息定制功能,您只需选择感兴趣的技术主题,即可获取重要资讯的。
相关厂商内容
相关赞助商
有了第二次浏览器大战中的佼佼者V8的适时助力,使得Node.js在短短的两年内达到可观的运行效率,并迅速被大家接受。这一点从Node.js项目在Github上的流行度和NPM上的库的数量可见一斑。
至于Node.js为何会选择Evented I/O for V8 JavaScript的结构和形式来实现,可以参见一下2011年初对作者Ryan Dahl的一次采访: 。
事件机制的实现
Node.js中大部分的模块,都继承自Event模块( )。Event模块(events.EventEmitter)是一个简单的事件监听器模式的实现。具有addListener/on,once,removeListener,removeAllListeners,emit等基本的事件监听模式的方法实现。它与前端DOM树上的事件并不相同,因为它不存在冒泡,逐层捕获等属于DOM的事件行为,也没有preventDefault()、stopPropagation()、 stopImmediatePropagation() 等处理事件传递的方法。
从另一个角度来看,事件侦听器模式也是一种事件钩子(hook)的机制,利用事件钩子导出内部数据或状态给外部调用者。Node.js中的很多对象,大多具有黑盒的特点,功能点较少,如果不通过事件钩子的形式,对象运行期间的中间值或内部状态,是我们无法获取到的。这种通过事件钩子的方式,可以使编程者不用关注组件是如何启动和执行的,只需关注在需要的事件点上即可。
var options = {
host: 'www.google.com',
path: '/upload',
method: 'POST'
var req = http.request(options, function (res) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('BODY: ' + chunk);
req.on('error', function (e) {
console.log('problem with request: ' + e.message);
// write data to request body
req.write('data\n');
req.write('data\n');
req.end();
在这段HTTP request的代码中,程序员只需要将视线放在error,data这些业务事件点即可,至于内部的流程如何,无需过于关注。
值得一提的是如果对一个事件添加了超过10个侦听器,将会得到一条警告,这一处设计与Node.js自身单线程运行有关,设计者认为侦听器太多,可能导致内存泄漏,所以存在这样一个警告。调用:
emitter.setMaxListeners(0);
可以将这个限制去掉。
其次,为了提升Node.js的程序的健壮性,EventEmitter对象对error事件进行了特殊对待。如果运行期间的错误触发了error事件。EventEmitter会检查是否有对error事件添加过侦听器,如果添加了,这个错误将会交由该侦听器处理,否则,这个错误将会作为异常抛出。如果外部没有捕获这个异常,将会引起线程的退出。
事件机制的进阶应用
继承event.EventEmitter
实现一个继承了EventEmitter类是十分简单的,以下是Node.js中流对象继承EventEmitter的例子:
function Stream() {
events.EventEmitter.call(this);
util.inherits(Stream, events.EventEmitter);
Node.js在工具模块中封装了继承的方法,所以此处可以很便利地调用。程序员可以通过这样的方式轻松继承EventEmitter对象,利用事件机制,可以帮助你解决一些问题。
多事件之间协作
在略微大一点的应用中,数据与Web服务器之间的分离是必然的,如新浪微博、Facebook、Twitter等。这样的优势在于数据源统一,并且可以为相同数据源制定各种丰富的客户端程序。以Web应用为例,在渲染一张页面的时候,通常需要从多个数据源拉取数据,并最终渲染至客户端。Node.js在这种场景中可以很自然很方便的同时并行发起对多个数据源的请求。
api.getUser(&username&, function (profile) {
// Got the profile
api.getTimeline(&username&, function (timeline) {
// Got the timeline
api.getSkin(&username&, function (skin) {
// Got the skin
Node.js通过异步机制使请求之间无阻塞,达到并行请求的目的,有效的调用下层资源。但是,这个场景中的问题是对于多个事件响应结果的协调并非被Node.js原生优雅地支持。为了达到三个请求都得到结果后才进行下一个步骤,程序也许会被变成以下情况:
api.getUser(&username&, function (profile) {
api.getTimeline(&username&, function (timeline) {
api.getSkin(&username&, function (skin) {
这将导致请求变为串行进行,无法最大化利用底层的API服务器。
为解决这类问题,我曾写作一个模块(EventProxy,)来实现多事件协作,以下为上面代码的改进版:
var proxy = new EventProxy();
proxy.all(&profile&, &timeline&, &skin&, function (profile, timeline, skin) {
api.getUser(&username&, function (profile) {
proxy.emit(&profile&, profile);
api.getTimeline(&username&, function (timeline) {
proxy.emit(&timeline&, timeline);
api.getSkin(&username&, function (skin) {
proxy.emit(&skin&, skin);
EventProxy也是一个简单的事件侦听者模式的实现,由于底层实现跟Node.js的EventEmitter不同,无法合并进Node.js中。但是却提供了比EventEmitter更强大的功能,且API保持与EventEmitter一致,与Node.js的思路保持契合,并可以适用在前端中。
这里的all方法是指侦听完profile、timeline、skin三个方法后,执行回调函数,并将侦听接收到的数据传入。
最后还介绍一种解决多事件协作的方案:Jscex( )。Jscex通过运行时编译的思路(需要时也可在运行前编译),将同步思维的代码转换为最终异步的代码来执行,可以在编写代码的时候通过同步思维来写,可以享受到同步思维的便利写作,异步执行的高效性能。如果通过Jscex编写,将会是以下形式:
var data = $await(Task.whenAll({
profile: api.getUser(&username&),
timeline: api.getTimeline(&username&),
skin: api.getSkin(&username&)
// 使用data.profile, data.timeline, data.skin
此节感谢Jscex作者@老赵()的指正和帮助。
利用事件队列解决雪崩问题
所谓雪崩问题,是在缓存失效的情景下,大并发高访问量同时涌入数据库中查询,数据库无法同时承受如此大的查询请求,进而往前影响到网站整体响应缓慢。那么在Node.js中如何应付这种情景呢。
var select = function (callback) {
db.select(&SQL&, function (results) {
callback(results);
以上是一句数据库查询的调用,如果站点刚好启动,这时候缓存中是不存在数据的,而如果访问量巨大,同一句SQL会被发送到数据库中反复查询,影响到服务的整体性能。一个改进是添加一个状态锁。
var status = &ready&;
var select = function (callback) {
if (status === &ready&) {
status = &pending&;
db.select(&SQL&, function (results) {
callback(results);
status = &ready&;
但是这种情景,连续的多次调用select发,只有第一次调用是生效的,后续的select是没有数据服务的。所以这个时候引入事件队列吧:
var proxy = new EventProxy();
var status = &ready&;
var select = function (callback) {
proxy.once(&selected&, callback);
if (status === &ready&) {
status = &pending&;
db.select(&SQL&, function (results) {
proxy.emit(&selected&, results);
status = &ready&;
这里利用了EventProxy对象的once方法,将所有请求的回调都压入事件队列中,并利用其执行一次就会将监视器移除的特点,保证每一个回调只会被执行一次。对于相同的SQL语句,保证在同一个查询开始到结束的时间中永远只有一次,在这查询期间到来的调用,只需在队列中等待数据就绪即可,节省了重复的数据库调用开销。由于Node.js单线程执行的原因,此处无需担心状态问题。这种方式其实也可以应用到其他远程调用的场景中,即使外部没有缓存策略,也能有效节省重复开销。此处也可以用EventEmitter替代EventProxy,不过可能存在侦听器过多,引发警告,需要调用setMaxListeners(0)移除掉警告,或者设更大的警告阀值。
田永强,新浪微博,前端工程师,曾就职于SAP,现就职于淘宝,花名朴灵,致力于NodeJS和Mobile Web App方面的研发工作。双修前后端JavaScript,寄望将NodeJS引荐给更多的工程师。兴趣:读万卷书,行万里路。个人Github地址:。
感谢对本文的审校。
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至。也欢迎大家通过新浪微博()或者腾讯微博()关注我们,并与我们的编辑和其他读者朋友交流。
Author Contacted
语言 & 开发
417 他的粉丝
深入浅出Node.js
16 他的粉丝
2 他的粉丝
JavaScript
64 他的粉丝
2 他的粉丝
0 他的粉丝
告诉我们您的想法
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
Re: 好文章
编辑,配错图了
Re: 好文章
Re: 好文章
同一个对象怎么通过序列化传递,保持服务器与浏览器端一致性?
最后一段代码的疑惑
Re: 好文章
为什么要显示调用events.EventEmitter.call
Re: 最后一段代码的疑惑
Re: 为什么要显示调用events.EventEmitter.call
Re: 为什么要显示调用events.EventEmitter.call
以下代码后3个方法中后两个参数应该传错了吧???
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
赞助商链接
InfoQ每周精要
订阅InfoQ每周精要,加入拥有25万多名资深开发者的庞大技术社区。
架构 & 设计
文化 & 方法
InfoQ.com及所有内容,版权所有 ©
C4Media Inc. InfoQ.com 服务器由 提供, 我们最信赖的ISP伙伴。
极客邦控股(北京)有限公司
找回密码....
InfoQ账号使用的E-mail
关注你最喜爱的话题和作者
快速浏览网站内你所感兴趣话题的精选内容。
内容自由定制
选择想要阅读的主题和喜爱的作者定制自己的新闻源。
设置通知机制以获取内容更新对您而言是否重要
注意:如果要修改您的邮箱,我们将会发送确认邮件到您原来的邮箱。
使用现有的公司名称
修改公司名称为:
公司性质:
使用现有的公司性质
修改公司性质为:
使用现有的公司规模
修改公司规模为:
使用现在的国家
使用现在的省份
Subscribe to our newsletter?
Subscribe to our industry email notices?
我们发现您在使用ad blocker。
我们理解您使用ad blocker的初衷,但为了保证InfoQ能够继续以免费方式为您服务,我们需要您的支持。InfoQ绝不会在未经您许可的情况下将您的数据提供给第三方。我们仅将其用于向读者发送相关广告内容。请您将InfoQ添加至白名单,感谢您的理解与支持。nodejs多房间web聊天室
Nodejs背景简介
1 ,JavaScript最早是运行在浏览器中,然而浏览器只是提供了一个上下文
2 ,node js事实上就是另外一种上下文,它允许在后端(脱离浏览器环境)运行JavaScript代码
3 ,Node js事实上既是一
Nodejs背景简介
1 ,Script最早是运行在中,然而浏览器只是提供了一个上下文
2 ,node.js事实上就是另外一种上下文,它允许在后端(脱离浏览器环境)运行代码
3 ,Node.js事实上既是一个运行时环境,同时又是一个库
Nodejs架构如下图
Node.js 的异步机制是基于事件的,所有的磁盘 I/O 、网络通信、查询都以非阻塞,的方式请求,返回的结果由事件循环来处理
&事件驱动的回调(事件轮询) &异步IO避免了频繁的上下文切换 &在node中除了代码,所有一切都是并行执行的
多线程同步式 I/O与单线程异步式 I/O
同步式 I/O (阻塞式):
利用多线程提供吞吐量
通过事件片分割和线程调度利用多核CPU
需要由操作调度多线程使用多核 CPU
难以充分利用 CPU 资源
内存轨迹大,数据局部性弱
符合线性的思维
异步式 I/O (非阻塞式):
单线程即可实现高吞吐量
通过功能划分利用多核CPU
可以将单进程绑定到单核 CPU
可以充分利用 CPU 资源
内存轨迹小,数据局部性强
不符合传统编程思维
Node.js 程序由事件循环开始,到事件循环结束,所有的逻辑都是事件的回调函数,所以 Node.js 始终在事件循环中,程序入口就是事件循环第一个事件的回调函数
Nodejs核心模块
1,核心模块是 Node.js 的心脏,它由一些精简而高效的库组成,为 Node.js 提供了基本的 API
2,process:用于描述当前 Node.js 进程状态的对象,提供了一个与操作系统的简单接口,通常在你写本地命令行程序的时候用到。
3,console :用于提供控制台标准输出。(IE)
4,Util: 是一个 Node.js 核心模块,提供常用函数的集合
5,events :是 Node.js 最重要的模块,没有“之一”
6,fs :文件系统,提供了文件的读取、写入、更名、删除、遍历目录、链接等 POSIX 文件系统操作
模块httpServer,Nodejs server 采用V8
var http = require(http);//请求(require)Node.js自带的 http 模块,并且把它赋值给 http 变量
http.createServer(function(request, response) {
//调用http模块提供的函数: createServer
response.writeHead(200, {Content-Type: text/plain});
response.write(Hello World);
response.end();
}).listen(8888);
socket.io 提供了三种默认的事件:connect 、message 、disconnect 。
当与对方建立连接后自动触发 connect 事件,当收到对方发来的数据后触发 message 事件(通常为 socket.send() 触发),当对方关闭连接后触发 disconnect 事件。
socket.emit() :向建立该连接的客户端广播
socket.broadcast.emit() :向除去建立该连接的客户端的所有客户端广播
io.sockets.emit() :向所有客户端广播,等同于上面两个的和
CLIENT (INDEX.HTML)
&script src=/socket.io/socket.io.js&&/script& &script&
varsocket=io.connect('http://localhost'); socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
SERVER (APP.JS)
var app = require('express')() ,
server = require('http').createServer(app) ,
io = require('socket.io').listen(server);
//将 socket.io 绑定到服务器上
server.listen(80);
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
//服务器监听所有客户端,并返回该新连接对象
io.sockets.on('connection', function (socket) {
socket.emit('news', { hello: 'world'
socket.on('my other event', function (data) {
console.log(data);
聊天室功能图
红黑联盟&版权所有
Copyright&& 2017
All rights reserved.Node.js 教程
Node.js EventEmitter
Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。
Node.js里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时分发一个事件, 一个fs.readStream对象会在文件被打开的时候发出一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。
EventEmitter 类
events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。
你可以通过require("events");来访问该模块。
// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();
EventEmitter 对象如果在实例化时发生错误,会触发 error 事件。当添加新的监听器时,newListener 事件会触发,当监听器被移除时,removeListener 事件被触发。
下面我们用一个简单的例子说明 EventEmitter 的用法:
//event.js 文件
var EventEmitter = require('events').EventE
var event = new EventEmitter();
event.on('some_event', function() {
&&&&console.log('some_event 事件触发');
setTimeout(function() {
&&&&event.emit('some_event');
执行结果如下:
运行这段代码,1 秒后控制台输出了 'some_event 事件触发'。其原理是 event 对象注册了事件 some_event 的一个监听器,然后我们通过 setTimeout 在 1000 毫秒以后向 event 对象发送事件 some_event,此时会调用some_event 的监听器。
$ node event.js
some_event 事件触发
EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter 支持 若干个事件监听器。
当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。
让我们以下面的例子解释这个过程:
//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 参数');
执行以上代码,运行的结果如下:
$ node event.js
listener1 arg1 参数 arg2 参数
listener2 arg1 参数 arg2 参数
以上例子中,emitter 为事件 someEvent 注册了两个事件监听器,然后触发了 someEvent 事件。运行结果中可以看到两个事件监听器回调函数被先后调用。 这就是EventEmitter最简单的用法。
EventEmitter 提供了多个属性,如 on 和 emit。on 函数用于绑定事件函数,emit 属性用于触发一个事件。接下来我们来具体看下 EventEmitter 的属性介绍。
序号方法 & 描述
1addListener(event, listener)
为指定事件添加一个监听器到监听器数组的尾部。
2on(event, listener)为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。
server.on('connection', function (stream) {
console.log('someone connected!');
3once(event, listener)为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。
server.once('connection', function (stream) {
console.log('Ah, we have our first user!');
4removeListener(event, listener)移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。它接受两个参数,第一个是事件名称,第二个是回调函数名称。
var callback = function(stream) {
console.log('someone connected!');
server.on('connection', callback);
server.removeListener('connection', callback);
5removeAllListeners([event])移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。
6setMaxListeners(n)默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。
setMaxListeners 函数用于提高监听器的默认限制的数量。
7listeners(event)返回指定事件的监听器数组。
8emit(event, [arg1], [arg2], [...])按参数的顺序执行每个监听器,如果事件有注册监听返回 true,否则返回 false。
序号方法 & 描述
1listenerCount(emitter, event)返回指定事件的监听器数量。
序号事件 & 描述
1newListener
event - 字符串,事件名称
listener - 处理事件函数
该事件在添加新监听器时被触发。
2removeListener
event - 字符串,事件名称
listener - 处理事件函数
从指定监听器数组中删除一个监听器。需要注意的是,此操作将会改变处于被删监听器之后的那些监听器的索引。
以下实例通过 connection(连接)事件演示了 EventEmitter 类的应用。
创建 main.js 文件,代码如下:
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 = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " 个监听器监听连接事件。");
// 处理 connection 事件
eventEmitter.emit('connection');
// 移除监绑定的 listener1 函数
eventEmitter.removeListener('connection', listener1);
console.log("listener1 不再受监听。");
// 触发连接事件
eventEmitter.emit('connection');
eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " 个监听器监听连接事件。");
console.log("程序执行完毕。");
以上代码,执行结果如下所示:
$ node main.js
2 个监听器监听连接事件。
监听器 listener1 执行。
监听器 listener2 执行。
listener1 不再受监听。
监听器 listener2 执行。
1 个监听器监听连接事件。
程序执行完毕。
error 事件
EventEmitter 定义了一个特殊的事件 error,它包含了错误的语义,我们在遇到
异常的时候通常会触发 error 事件。
当 error 被触发时,EventEmitter 规定如果没有响
应的监听器,Node.js 会把它当作异常,退出程序并输出错误信息。
我们一般要为会触发 error
事件的对象设置监听器,避免遇到错误后整个程序崩溃。例如:
var events = require('events');
var emitter = new events.EventEmitter();
emitter.emit('error');
运行时会显示以下错误:
node.js:201
// 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)
继承 EventEmitter
大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、
http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。
为什么要这样做呢?原因有两点:
首先,具有某个实体功能的对象实现事件符合语义,
事件的监听和发生应该是一个对象的方法。
其次 JavaScript 的对象机制是基于原型的,支持
部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。
5个月前 (01-10)
感谢您的支持,我会继续努力的!
扫码打赏,你说多少就多少
记住登录状态
重复输入密码}

我要回帖

更多关于 nodejs emit 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信