搜索
首页web前端js教程Node.js中的事件监听和事件发布用法实例详解

Node.js中的事件监听和事件发布用法实例详解

Jul 24, 2017 am 10:29 AM
javascriptnode.js监听

node.js是基于单线程无阻塞异步式的I/O,异步式的I/O指的是当遇到I/O操作的时候,线程不阻塞而是进行下面的操作,那么I/O操作完成之后,线程时如何知道该操作完成的呢?

当操作完成耗时的I/O操作之后,会以事件的形式通知I/O操作的线程完成,线程会在特定的时候来处理这个事件,进行下一步的操作,为了完成异步I/O,线程必须有事件循环的机制,不停的坚持是否有没有完成的事件,依次完成这些事件的处理。

而对于阻塞式I/O,线程遇到耗时的I/O操作会停止继续执行,等待操作的完成,这个时候线程就不能接受其他的操作请求,为了提供吞吐量,必须创建多个线程,每个线程去响应一个客户的请求,但是同一时间,一个cpu核心上面只能运行一个线程,多个线程要想执行就必须在不同的线程之间进行切换。

因此node.js少了多线程中线程的创建,以及线程的切换的开销,线程切换的代价是非常大的,需要为其分配内存,列入调度,同时在线程切换的时候需要执行内存换页等等操作,采用单线程的方式就可以减少这些操作。但是这种编程方式也有缺点,不符合人们的设计思维。

node.js是基于事件的模式来实现异步I/O的,当其启动之后会不停的遍历是否有为完成的事件,然后进行执行,执行完成之后会以另外一个事件的形式通知线程,本操作已经完成,这个事件又会被添加到未完成的事件列表中,线程在接下来的某个时刻遍历到这个事件然后进行执行,在这种机制中,需要将一个大的任务分成一个个小的事件,node.js也适合处理一些高I/O,低逻辑的场景。

下面的例子演示异步的文件读取:


var fs = require('fs'); 
fs.readFile('file.txt', 'utf-8', function(err, data) { 
if (err) { 
<span style="white-space:pre"> </span>console.error(err); 
} else { 
<span style="white-space:pre"> </span>console.log(data); 
} 
}); 
[javascript] view plain copy
console.log("end");

如上fs.readFile异步读取文件,之后流程就会继续走,并不会等待其读取完文件,当文件读取完毕之后,会发布一个事件,执行线程遍历到该事件就会去执行对应的操作,这里是执行相应的回调函数,例子中字符串end会比文件内容先打印出来。

node.js的事件API

events.EventEmitter:EventEmitter对node.js中的事件发射与事件监听功能提供了封装,每个事件由一个标识事件名的字符串和对应的操作组成。

事件的监听:


var events = require("events"); 
var emitter = new events.EventEmitter(); 
 <span style="font-family: Arial, Helvetica, sans-serif;">emitter.on("eventName", function(){</span> 
  console.log("eventName事件发生") 
})

事件的发布:


emitter.emit("eventName");

发布事件的时候我们可以传入多个参数,第一个参数表示事件的名称,其后的参数表示传入的参数,这些参数会被传入到事件的回调函数中。

EventEmitter.once("eventName", listener) :为事件注册一个只执行一次的监听器,当事件第一次发生并触发监听器之后,该监听器就会解除,之后如果事件发生,该监听器不会执行。

EventEmitter.removeListener(event, listener) :移除掉事件的监听器

EventEmitter.removeAllListeners(event) :移除掉事件的所有的监听器

EventEmitter.setMaxListeners(n) :node.js默认单个事件最大的监听器个数是10,如果超过10会给予警告,这么做是为了防止内存的溢出,我们可以更改这种限制设置为其他的数字,如果设置为0表示不进行限制。

EventEmitter.listeners(event) :返回某个事件的监听器列表

多事件之间协作
在略微大一点的应用中,数据与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) {
   // TODO
  });
 });
});

这将导致请求变为串行进行,无法最大化利用底层的API服务器。

为解决这类问题,我曾写作一个模块来实现多事件协作,以下为上面代码的改进版:


var proxy = new EventProxy();
proxy.all("profile", "timeline", "skin", function (profile, timeline, skin) {
 // TODO
});
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三个方法后,执行回调函数,并将侦听接收到的数据传入。

利用事件队列解决雪崩问题

所谓雪崩问题,是在缓存失效的情景下,大并发高访问量同时涌入数据库中查询,数据库无法同时承受如此大的查询请求,进而往前影响到网站整体响应缓慢。

那么在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)移除掉警告,或者设更大的警告阀值。

以上是Node.js中的事件监听和事件发布用法实例详解的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
Java vs JavaScript:开发人员的详细比较Java vs JavaScript:开发人员的详细比较May 16, 2025 am 12:01 AM

javaandjavascriptaredistinctlanguages:javaisusedforenterpriseandmobileapps,while javascriptifforInteractiveWebpages.1)JavaisComcompoppored,statieldinglationallyTypted,statilly tater astrunsonjvm.2)

JavaScript数据类型:浏览器和nodejs之间是否有区别?JavaScript数据类型:浏览器和nodejs之间是否有区别?May 14, 2025 am 12:15 AM

JavaScript核心数据类型在浏览器和Node.js中一致,但处理方式和额外类型有所不同。1)全局对象在浏览器中为window,在Node.js中为global。2)Node.js独有Buffer对象,用于处理二进制数据。3)性能和时间处理在两者间也有差异,需根据环境调整代码。

JavaScript评论:使用//和 / * * / * / * /JavaScript评论:使用//和 / * * / * / * /May 13, 2025 pm 03:49 PM

JavaScriptusestwotypesofcomments:single-line(//)andmulti-line(//).1)Use//forquicknotesorsingle-lineexplanations.2)Use//forlongerexplanationsorcommentingoutblocksofcode.Commentsshouldexplainthe'why',notthe'what',andbeplacedabovetherelevantcodeforclari

Python vs. JavaScript:开发人员的比较分析Python vs. JavaScript:开发人员的比较分析May 09, 2025 am 12:22 AM

Python和JavaScript的主要区别在于类型系统和应用场景。1.Python使用动态类型,适合科学计算和数据分析。2.JavaScript采用弱类型,广泛用于前端和全栈开发。两者在异步编程和性能优化上各有优势,选择时应根据项目需求决定。

Python vs. JavaScript:选择合适的工具Python vs. JavaScript:选择合适的工具May 08, 2025 am 12:10 AM

选择Python还是JavaScript取决于项目类型:1)数据科学和自动化任务选择Python;2)前端和全栈开发选择JavaScript。Python因其在数据处理和自动化方面的强大库而备受青睐,而JavaScript则因其在网页交互和全栈开发中的优势而不可或缺。

Python和JavaScript:了解每个的优势Python和JavaScript:了解每个的优势May 06, 2025 am 12:15 AM

Python和JavaScript各有优势,选择取决于项目需求和个人偏好。1.Python易学,语法简洁,适用于数据科学和后端开发,但执行速度较慢。2.JavaScript在前端开发中无处不在,异步编程能力强,Node.js使其适用于全栈开发,但语法可能复杂且易出错。

JavaScript的核心:它是在C还是C上构建的?JavaScript的核心:它是在C还是C上构建的?May 05, 2025 am 12:07 AM

javascriptisnotbuiltoncorc; saninterpretedlanguagethatrunsonenginesoftenwritteninc.1)javascriptwasdesignedAsalightweight,解释edganguageforwebbrowsers.2)Enginesevolvedfromsimpleterterterpretpreterterterpretertestojitcompilerers,典型地提示。

JavaScript应用程序:从前端到后端JavaScript应用程序:从前端到后端May 04, 2025 am 12:12 AM

JavaScript可用于前端和后端开发。前端通过DOM操作增强用户体验,后端通过Node.js处理服务器任务。1.前端示例:改变网页文本内容。2.后端示例:创建Node.js服务器。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)