Node.js 教程 05 - EventEmitter(事件监听/发射器 )
目录:
前言:
今天事儿太多了,没有发太多的东西。不好意思了各位。
本篇主要介绍Node.js中的事件驱动,至于Node.js事件概念的东西,太多了。
本系列课程主要抱着的理念就是,让大家慢慢的入门,我也尽量写的简单一点。
所以呢,本文事件驱动,大家的目标应该是:理解Node.js的事件驱动、会使用注册事件及发射事件即可。
其他的只作为了解,在这里就一笔而过了,如果大家想深入了解,请自行百度。
Node.js事件驱动介绍:
Node.js在Github上有一句短短的介绍:Evented I/O for V8 Javascript。
一句话,霸气侧漏:基于V8引擎实现的事件驱动I/O,因此,Node.js也以事件驱动著名、通过异步的编程达到高吞吐量高性能。
Node.js能在众多的后端Javascript技术中脱颖而出,正是因其事件的特点而受到欢迎。
拿Rhino来做比较,可以看出Rhino引擎支持的后端JavaScript摆脱不掉其他语言同步执行的影响,导致JavaScript在后端编程与前端编程之间有着十分显著的差别,在编程模型上无法形成统一。
在前端编程中,事件的应用十分广泛,DOM上的各种事件。在Ajax大规模应用之后,异步请求更得到广泛的认同,而Ajax亦是基于事件机制的。
在Rhino中,文件读取等操作,均是同步操作进行的。在这类单线程的编程模型下,如果采用同步机制,无法与PHP之类的服务端脚本语言的成熟度媲美,性能也没有值得可圈可点的部分。
直到Ryan Dahl在2009年推出Node.js后,后端JavaScript才走出其迷局。
Node.js的推出,我觉得该变了两个状况:
- 统一了前后端JavaScript的编程模型。
- 利用事件机制充分利用用异步IO突破单线程编程模型的性能瓶颈,使得JavaScript在后端达到实用价值。
有了第二次浏览器大战中的佼佼者V8的适时助力,使得Node.js在短短的两年内达到可观的运行效率,并迅速被大家接受。这一点从Node.js项目在Github上的流行度和NPM上的库的数量可见一斑。
Node.js事件:
Node.js中,所有异步的I/O操作,在完成的时候都会发送一个事件到事件队列中。
Node.js中的许多对象也都会分发事件,比如:
1. net.Server 对象会在每次有新链接时分发一个事件;
2. fs.readStream 对象会在文件被打开的时候分发一个事件;
3.。。。。。。
所有这些产生事件的对象都是 event.EventEmitter (事件监听/发射器)的实例。我们可以通过“ require('events') ”来访问该模块。
Event模块(event.EventEmitter)是一个简单的事件监听器模式的实现,具有 addListener 、 on 、 once 、 removelistener 、 removeAllListener 、 emit 等基本的事件监听模式的方法实现。
它与前端DOM树上的事件并不相同,因为它不存在事件冒泡,逐层捕获等属于DOM的事件行为,也没有preventDefault()、stopPropagation()、stopImmediatePropagation()等处理事件传递的方法。
从另一个角度来看,事件侦听器模式也是一种事件钩子(hook)的机制,利用事件钩子导出内部数据或状态给外部调用者。
Node.js中的很多对象,大多具有黑盒的特点,功能点较少,如果不通过事件钩子的形式,对象运行期间的中间值或内部状态,是我们无法获取到的。
这种通过事件钩子的方式,可以使编程者不用关注组件是如何启动和执行的,只需关注在需要的事件点上即可。
回顾我们之前创建HTTP服务器的Node.js代码,小动更改,新建app.js代码如下:
var http = require("http"); function onRequest(request, response){ console.log("Request received."); response.writeHead(200, {"Content-Type" : "text/plain"}); response.write("Hello World!"); response.end(); } http.createServer(onRequest).listen(88); console.log("Server has started!");
然后我们运行“node app.js”,访问浏览器即可看到效果:
而当我们刷新页面时,服务端便会创建一个请求:
【分析:(大家不要死扣这部分内容,只需要大概了解Node.js中事件的特点 - “事件轮询机制”就OK了。)】
这段代码中,我们使用函数onRequest封装了请求的处理部分。当我们启动它会立即输出“Server started!”。在我们的浏览器访问http://128.0.0.1:88时,会显示消息“Request received。”
这两个代码的主要区别是,前者将处理部分写在 http.createServer 中,按照传统思路,启动服务器后,遇到这段代码会去运行,如果运行时间很长,导致暂停,非常没有效率。如果第二位用户请求的服务器,而它仍然在服务第一个请求,那第二个请求只能回答第一个完成后才能应答,这就是堵塞式Socket IO的问题。
Node.js的通过一个低级别的C / C + +层将异步执行有关IO的操作,一旦监听到请求, Node.js将执行您作为参数传递到I / O操作函数的回调函数,如上面的onRequest。这个异步操作关键是基于事件轮询机制。
关于事件轮询机制,内部覆盖比较广,我也就不增加本文篇幅了。如果感兴趣,大家可以自行百度,网上有案例讲解。
注册并发射自定义Node.js事件:
我们现在要做的实例就是:使用Node.js注册一个用户自定义事件,然后再使用Node.js发射这个自定义事件。
步骤1:新建app.js,代码如下:
var EventEmitter = require('events').EventEmitter; // 引入事件模块 var event = new EventEmitter(); // 实例化事件模块 // 注册事件(customer_event) event.on('customer_event', function() { console.log('customer_event has be occured : ' + new Date()); }); setInterval(function() { event.emit('customer_event'); // 发射(触发)事件(customer_event) }, 500);
上述代码中,我们首先使用require引入事件模块的EventEmitter(注册/发射器)。
然后,我们实例化EventEmitter对象,存入到本地变量event中。
然后,我们使用event对象的on函数,注册一个名为“customer_event”的自定义事件,事件的动作为输出一段信息。
最后,我们使用setInterval函数,每500ms循环调用event对象的emit函数,来发射(触发)我们自定义的“customer_event”事件。
步骤2:运行“node app.js”,效果如下:
这个应该很好理解吧。
到此为止,我们知道了:使用EventEmitter对象的on注册事件,然后使用对象的emit发射事件。
EventEmitter介绍:
events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件发射与事件监听器功能的封装。
我们直接上例子吧。边看边说。
步骤1:新建app.js主程序,代码如下:
var EventEmitter = require('events').EventEmitter; // 引入事件模块 var event = new EventEmitter(); // 实例化事件模块 // 注册事件(sayHello) event.on('sayHello', function(param1, param2) { console.log('Hello1 : ', param1, param2); }); // 再次注册事件(sayHello) event.on('sayHello', function(param1, param2) { console.log('Hello2 : ', param1, param2); }); event.emit('sayHello', 'GuYing', '1996'); // 发射(触发)事件(sayHello)
上述代码中,有两点值得介绍的是事件参数,其次您可能觉得比较别扭的是,这个事件注册了两次!
EventEmitter 的每一个事件都是由一个事件名和若干个参数组成。事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter 支持若干个事件监听器。
当事件发射时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。
步骤2:运行“node app.js”,执行效果如下:
以上例子中,emitter 为事件 someEvent 注册了两个事件监听器,然后发射了 someEvent 事件。
运行结果中可以看到两个事件监听器回调函数被先后调用。 这就是EventEmitter最简单的用法。
EventEmitter常用的API:
EventEmitter.on(event, listener)、emitter.addListener(event, listener) 为指定事件注册一个监听器,接受一个字 符串 event 和一个回调函数 listener。
server.on('connection', function (stream) { console.log('someone connected!'); });
EventEmitter.emit(event, [arg1], [arg2], [...]) 发射 event 事件,传 递若干可选参数到事件监听器的参数表。
EventEmitter.once(event, listener) 为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。
server.once('connection', function (stream) { console.log('Ah, we have our first user!'); });
EventEmitter.removeListener(event, listener) 移除指定事件的某个监听 器,listener 必须是该事件已经注册过的监听器。
var callback = function(stream) { console.log('someone connected!'); }; server.on('connection', callback); // ... server.removeListener('connection', callback);
EventEmitter.removeAllListeners([event]) 移除所有事件的所有监听器, 如果指定 event,则移除指定事件的所有监听器。
error事件:
EventEmitter 定义了一个特殊的事件 error,它包含了"错误"的语义,我们在遇到 异常的时候通常会发射 error 事件。
当 error 被发射时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并打印调用栈。
我们一般要为会发射 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。例如:
var events = require('events'); var emitter = new events.EventEmitter(); emitter.emit('error');
运行时会显示以下错误:
node.js: throw e; // process.nextTick error, or 'error' event on first tick ^ Error: Uncaught, unspecified 'error' event. at EventEmitter.emit (events.js::) at Object.<anonymous> (/home/byvoid/error.js::) at Module._compile (module.js::) at Object..js (module.js::) at Module.load (module.js::) at Function._load (module.js::) at Array. (module.js::) at EventEmitter._tickCallback (node.js::)
继承EventEmitter:
大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。
为什么要这样做呢?原因有两点:
首先,具有某个实体功能的对象实现事件符合语义, 事件的监听和发射应该是一个对象的方法。
其次JavaScript 的对象机制是基于原型的,支持 部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。
Node.js 教程 05 - EventEmitter(事件监听/发射器 )的更多相关文章
- React.js 小书 Lesson9 - 事件监听
作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson9 转载请注明出处,保留原文链接和作者信息. 在 React.js 里面监听事件是很容易的事情 ...
- onscroll事件没有响应的原因以及vue.js中添加onscroll事件监听的方法
1 onscroll事件失效 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...
- node.js 基础二 开启服务器监听
1.server.js 2.监听 一 server.js 二 监听 运行server.js后,浏览器打开:http://localhost:8888/ //====================== ...
- Node.js 教程 01 - 简介、安装及配置
系列目录: Node.js 教程 01 - 简介.安装及配置 Node.js 教程 02 - 经典的Hello World Node.js 教程 03 - 创建HTTP服务器 Node.js 教程 0 ...
- Node.js自定义对象事件监听与发射
一.Node.js是以事件驱动的,那我们自定义的一些js对象就需要能监听事件以及发射事件.在Node.js中事件使用一个EventEmitter对象发出,该对象在events模块中.它应该是使用观察者 ...
- 简单剖析Node中的事件监听机制(一)
使用js的class类简单的实现一个事件监听机制,不同于浏览器中的时间绑定与监听,类似于node中的时间监听,并且会在接下来的文章中去根据自己的理解去写一下Event模块中的原理. Node.js使用 ...
- JS事件流、事件监听、事件对象、事件委托
JS事件流: 01.DOM级别和DOM事件 02.JS事件流:页面中接收事件的顺序 事件冒泡阶段-->处于目标阶段-->事件捕获阶段 (事件捕获总发生在事件冒泡前面) 03.捕获:从外向里 ...
- [JS]笔记12之事件机制--事件冒泡和捕获--事件监听--阻止事件传播
-->事件冒泡和捕获-->事件监听-->阻止事件传播 一.事件冒泡和捕获 1.概念:当给子元素和父元素定义了相同的事件,比如都定义了onclick事件,点击子元素时,父元素的oncl ...
- [No00006A]Js的addEventListener()及attachEvent()区别分析【js中的事件监听】
1.添加时间监听: Chrom中: addEventListener的使用方式: target.addEventListener(type, listener, useCapture); target ...
随机推荐
- Chrome 控制台不完全指南
Chrome的开发者工具已经强大到没朋友的地步了,特别是其功能丰富界面友好的console,使用得当可以有如下功效: 更高「逼格」更快「开发调试」更强「进阶级的Frontender」 Bug无处遁形「 ...
- PHP安全之Web攻击
一.SQL注入攻击(SQL Injection) 攻击者把SQL命令插入到Web表单的输入域或页面请求的字符串,欺骗服务器执行恶意的SQL命令.在某些表单中,用户输入的内容直接用来构造(或者影响)动态 ...
- JavaScript权威设计--Window对象(简要学习笔记十三)
1.Window对象是所有客户端JavaScript特性和API的主要接入点. Window对象中的一个重要属性是document,它引用Document对象. JavaScript程序可以通过Doc ...
- 前端学PHP之MemCache
× 目录 [1]作用 [2]安装 [3]管理[4]命令 前面的话 Memcache是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括 ...
- 【目录】本博客其他.NET开源项目文章目录
本博客所有文章分类的总目录链接:本博客博文总目录-实时更新 1.本博客其他.NET开源项目文章目录 37..NET平台开源项目速览(17)FluentConsole让你的控制台酷起来 36..NET平 ...
- TortoiseSVN的使用
1.安装和下载client客户端 (1)下载windows端程序:http://tortoisesvn.net/downloads.一般而言,如果是32bit操作系统,运行TortoiseSVN-1. ...
- Oracle使用SQL传输表空间
源环境:RHEL 6.4 + Oracle 11.2.0.4 目的环境:RHEL 6.4 + Oracle 11.2.0.4 DG双机 要求:使用SQL传输表空间DBS_D_JINGYU从源环境到目的 ...
- Bootstrap框架的学习(一)
一.Bootstrap框架介绍 Bootstrap是一个非常优秀的前端UI框架,一个轻量级的UI前端框架,是基于HTML+CSS+JavaScript的框架. 二.简单介绍 Bootstrap框架是属 ...
- Scrapy:为spider指定pipeline
当一个Scrapy项目中有多个spider去爬取多个网站时,往往需要多个pipeline,这时就需要为每个spider指定其对应的pipeline. [通过程序来运行spider],可以通过修改配置s ...
- [深入JUnit] 测试运行的入口
阅读前提 了解JUnit 对JUnit的内部实现有兴趣 不妨看看[深入JUnit] @Before, @After, @Test的秘密] 代码版本: junit 4.12代码搜索工具: http:// ...