热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Node.js教程第五讲:深入解析EventEmitter(事件监听与发射机制)

本文将深入探讨Node.js中的EventEmitter模块,详细介绍其在事件监听与发射机制中的应用。内容涵盖事件驱动的基本概念、如何在Node.js中注册和触发自定义事件,以及EventEmitter的核心API和使用方法。通过本教程,读者将能够全面理解并熟练运用EventEmitter进行高效的事件处理。

 

目录:

前言

Node.js事件驱动介绍

Node.js事件

注册并发射自定义Node.js事件

EventEmitter介绍

EventEmitter常用的API

error事件

继承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的推出,我觉得该变了两个状况:

  1. 统一了前后端Javascript的编程模型。
  2. 利用事件机制充分利用用异步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,代码如下:

 1 var EventEmitter = require('events').EventEmitter;     // 引入事件模块
 2 
 3 var event = new EventEmitter();     // 实例化事件模块
 4 
 5 // 注册事件(customer_event)
 6 event.on('customer_event', function() { 
 7     console.log('customer_event has be occured : ' + new Date()); 
 8 }); 
 9 
10 setInterval(function() { 
11     event.emit('customer_event');     // 发射(触发)事件(customer_event)
12 }, 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主程序,代码如下:

 1 var EventEmitter = require('events').EventEmitter;     // 引入事件模块
 2 
 3 var event = new EventEmitter();     // 实例化事件模块
 4 
 5 // 注册事件(sayHello)
 6 event.on('sayHello', function(param1, param2) { 
 7     console.log('Hello1 : ', param1, param2); 
 8 }); 
 9 
10 // 再次注册事件(sayHello)
11 event.on('sayHello', function(param1, param2) { 
12     console.log('Hello2 : ', param1, param2); 
13 }); 
14 
15 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: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. (/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 不会打乱对象原有的继承关系。

 


推荐阅读
  • 全面解析Java虚拟机:内存模型深度剖析 ... [详细]
  • 基于Node.js的高性能实时消息推送系统通过集成Socket.IO和Express框架,实现了高效的高并发消息转发功能。该系统能够支持大量用户同时在线,并确保消息的实时性和可靠性,适用于需要即时通信的应用场景。 ... [详细]
  • 本文详细探讨了Java集合框架的使用方法及其性能特点。首先,通过关系图展示了集合接口之间的层次结构,如`Collection`接口作为对象集合的基础,其下分为`List`、`Set`和`Queue`等子接口。其中,`List`接口支持按插入顺序保存元素且允许重复,而`Set`接口则确保元素唯一性。此外,文章还深入分析了不同集合类在实际应用中的性能表现,为开发者选择合适的集合类型提供了参考依据。 ... [详细]
  • 本文详细解析了如何使用 jQuery 实现一个在浏览器地址栏运行的射击游戏。通过源代码分析,展示了关键的 JavaScript 技术和实现方法,并提供了在线演示链接供读者参考。此外,还介绍了如何在 Visual Studio Code 中进行开发和调试,为开发者提供了实用的技巧和建议。 ... [详细]
  • 本题库精选了Java核心知识点的练习题,旨在帮助学习者巩固和检验对Java理论基础的掌握。其中,选择题部分涵盖了访问控制权限等关键概念,例如,Java语言中仅允许子类或同一包内的类访问的访问权限为protected。此外,题库还包括其他重要知识点,如异常处理、多线程、集合框架等,全面覆盖Java编程的核心内容。 ... [详细]
  • 前端技术实现调用摄像头进行拍照功能
    在公司项目中,为了实现调用摄像头进行拍照的功能,我们深入研究了HTML5的相关技术。尽管Java在许多方面表现出色,但在这一场景下,HTML5的灵活性和易用性更胜一筹。本文将分享具体的代码设计和实现细节,帮助开发者快速掌握这一功能。 ... [详细]
  • 本文详细解析了JSONP(JSON with Padding)的跨域机制及其工作原理。JSONP是一种通过动态创建``标签来实现跨域请求的技术,其核心在于利用了浏览器对``标签的宽松同源策略。文章不仅介绍了JSONP的产生背景,还深入探讨了其具体实现过程,包括如何构造请求、服务器端如何响应以及客户端如何处理返回的数据。此外,还分析了JSONP的优势和局限性,帮助读者全面理解这一技术在现代Web开发中的应用。 ... [详细]
  • 深入解析 OpenCV 2 中 Mat 对象的类型、深度与步长属性
    在OpenCV 2中,`Mat`类作为核心组件,对于图像处理至关重要。本文将深入探讨`Mat`对象的类型、深度与步长属性,这些属性是理解和优化图像操作的基础。通过具体示例,我们将展示如何利用这些属性实现高效的图像缩小功能。此外,还将讨论这些属性在实际应用中的重要性和常见误区,帮助读者更好地掌握`Mat`类的使用方法。 ... [详细]
  • PHP中元素的计量单位是什么? ... [详细]
  • Java服务问题快速定位与解决策略全面指南 ... [详细]
  • 在 HihoCoder 1505 中,题目要求从给定的 n 个数中选取两对数,使这两对数的和相等。如果直接对所有可能的组合进行遍历,时间复杂度将达到 O(n^4),因此需要考虑优化选择过程。通过使用哈希表或其他高效的数据结构,可以显著降低时间复杂度,从而提高算法的效率。具体实现中,可以通过预处理和存储中间结果来减少重复计算,进一步提升性能。 ... [详细]
  • ActiveMQ是由Apache开发的一款广受欢迎且功能强大的开源消息中间件。作为完全符合JMS 1.1和J2EE 1.4规范的JMS Provider实现,尽管JMS规范已问世多年,但ActiveMQ依然保持了其在消息队列领域的领先地位。本文将带你初步了解ActiveMQ的核心概念及其应用场景,帮助你快速入门这一重要的消息传递技术。 ... [详细]
  • 深入解析 JavaScript 代码执行流程:理解执行上下文与变量提升机制
    本文深入探讨了JavaScript代码的执行流程,重点解析了执行上下文和变量提升机制。通过详细分析代码解析过程,帮助开发者更好地理解JavaScript中的作用域和执行环境,为编写高效、无误的代码提供理论支持。 ... [详细]
  • 本文详细介绍了 Sublime Text 3 在 2021 年的激活密钥及其在线激活方法。用户可以通过提供的链接访问云海天教程,获取更多详细的激活码信息和操作步骤。此外,文章还提供了安全可靠的激活方案,帮助用户顺利激活软件,提升编程效率。 ... [详细]
  • Sublime Text 3 注册密钥及激活方法详解
    本文详细介绍了Sublime Text 3的注册密钥获取与激活方法,旨在帮助用户合法且高效地使用这款强大的文本编辑器。文章不仅提供了最新的注册密钥信息,还涵盖了详细的激活步骤,确保用户能够顺利激活软件,享受其带来的便捷与高效。此外,文中还简要对比了Sublime Text 3与其他主流文本编辑器的功能差异,为用户提供更多选择参考。 ... [详细]
author-avatar
尼玛的被注册了
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有