node 事件,node的事件循环机制

  node 事件,node的事件循环机制

  InfoQ:node . js的简单解释(4):node . js的事件机制。

  Node.js简单介绍(四):Node.js的事件机制

  Node.js Node.js的事件机制在其Github代码库(https://github.com/joyent/node)中有一个简短的介绍:Evented I/O for V8 JavaScript。这句类似广告语的话,道出了Node.js的独到之处:基于V8引擎的事件驱动IO。在本文的这一部分,让我来揭开“Evented”这个关键词的所有谜团。

  Node.js能在众多后端JavaScript技术中脱颖而出,也正是因为其基于事件的特性,才广受欢迎。对比Rhino可以看出,Rhino引擎支持的后端JavaScript无法摆脱其他语言同步执行的影响,导致后端编程与前端编程差异显著,编程模型无法统一。在前端编程中,事件被广泛使用,DOM上的各种事件。Ajax大规模应用后,异步请求得到了更广泛的认可,Ajax也是基于事件机制的。在Rhino中,文件读取等操作都是同步进行的。在这种单线程编程模式下,如果采用同步机制,则无法匹配PHP等服务器端脚本语言的成熟度,性能并不显著。直到Ryan Dahl在2009年推出Node.js,后端JavaScript才走出困惑。随着Node.js的推出,我觉得是时候改变两种情况了:

  统一了前后台JavaScript的编程模型。充分利用事件机制,用异步IO突破单线程编程模型的性能瓶颈,让JavaScript在后端实现实用价值。在第二次浏览器大战的领头羊V8的及时帮助下,Node.js在短短两年内取得了可观的运营效率,并迅速被大家所接受。这从Github上Node.js项目的受欢迎程度和NPM上的库数量就可以看出来。

  至于Node.js为什么选择V8 JavaScript的Evented I/O的结构和形式来实现,可以参考2011年初对作者Ryan Dahl的一次采访:http://bostinno . com/2011/01/31/node-js-interview-4-questions-with-creator-Ryan-Dahl/。

  事件机制的实现Node.js中的大部分模块都继承自事件模块(http://nodejs . org/docs/latest/API/events . html)。事件模块(事件。EventEmitter)是事件侦听器模式的简单实现。具有基本事件侦听模式的方法实现,如addListener/on、once、removeListener、removeAllListeners、emit等。和前端DOM树上的事件不一样,因为它没有冒泡、逐层捕捉等属于DOM的事件的行为,也没有preventDefault()、stopPropagation()、stopImmediatePropagation()等处理事件交付的方法。

  从另一个角度来看,事件侦听器模式也是事件钩子的一种机制,用于将内部数据或状态导出到外部调用方。Node.js中的很多对象都有黑盒的特点,功能点较少。如果它们不是以事件挂钩的形式出现,就无法获得对象在运行过程中的中间值或内部状态。这种方式,通过事件钩子,让程序员不用去关注组件是如何启动和执行的,而只关注需要的事件点。

  var选项={

  主持人: www.google.com ,

  端口:80,

  路径:“/上传”,

  方法:“发布”

  var req=http.request(options,function (res) {

  console . log( STATUS: RES . STATUS code );

  console . log( HEADERS: JSON . stringify(RES . HEADERS));

  RES . set encoding( utf8 );

  res.on(data ,function (chunk) {

  console . log( BODY: chunk );

  req.on(错误,函数(e) {

  console.log(请求问题: e . message );

  //将数据写入请求体

  req . write( data n );

  req . write( data n );

  req . end();在这段HTTP请求的代码中,程序员只需要关注错误、数据等业务事件,不要太关注内部流程。

  值得一提的是,如果您向一个事件添加超过10个侦听器,您将会得到一个警告。这样的设计和Node.js本身的单线程运行有关。设计者认为监听器太多可能导致内存泄漏,所以有这样的警告。调用:

  emitter . setmaxlisteners(0);这个限制可以取消。

  其次,为了提高Node.js程序的健壮性,EventEmitter对象对error事件进行了特殊处理。如果运行时的错误触发了错误事件。EventEmitter将检查是否有侦听器被添加到错误事件中。如果是,该错误将由侦听器处理;否则,该错误将作为异常抛出。如果这个异常没有被外部捕获,它将导致线程退出。

  事件机制的高级应用继承事件。EventEmitter实现一个继承EventEmitter的类非常简单。以下是Node.js中继承EventEmitter的流对象的示例:

  函数流(){

  事件。event emitter . call(this);

  util.inherits(流,事件。event emitter);Node.js将继承的方法封装在工具模块中,所以在这里可以方便地调用。程序员可以通过这种方式轻松继承EventEmitter对象。使用事件机制可以帮助您解决一些问题。

  多个事件间的协作在稍微大一点的应用中,数据和Web服务器的分离是不可避免的,比如新浪微博、脸书、Twitter等。这样做的好处是数据源统一,可以针对同一个数据源开发各种富客户端程序。以Web应用为例。在呈现页面时,通常需要从多个数据源提取数据并将其呈现给客户端。在这种情况下,Node.js可以自然而方便地并行发起对多个数据源的请求。

  api.getUser(用户名,函数(配置文件){

  //获取了配置文件

  api.getTimeline(用户名,函数(时间线){

  //得到时间线

  api.getSkin(用户名,函数(皮肤){

  //得到了皮肤

  });Node.js通过异步机制使请求畅通,达到并行请求的目的,有效调用下层资源。然而,这个场景中的问题是Node.js本身并不支持多个事件响应结果的协调。为了在进行下一步之前实现所有三个请求的结果,程序可能会被更改为以下情况:

  api.getUser(用户名,函数(配置文件){

  api.getTimeline(用户名,函数(时间线){

  api.getSkin(用户名,函数(皮肤){

  //TODO

  });这将导致请求变成串行的,不能充分利用底层的API服务器。

  为了解决这样的问题,我写了一个模块(https://github.com/JacksonTian/eventproxy)来实现多事件协作。以下是上述代码的改进版本:

  var proxy=new event proxy();

  proxy.all(profile , timeline , skin ,函数(profile,timeline,skin) {

  //TODO

  api.getUser(用户名,函数(配置文件){

  proxy.emit(profile ,profile);

  api.getTimeline(用户名,函数(时间线){

  proxy.emit(timeline ,timeline);

  api.getSkin(用户名,函数(皮肤){

  proxy.emit(skin ,皮肤);

  });EventProxy也是事件侦听器模式的一个简单实现。因为底层实现和Node.js的EventEmitter不一样,所以不能合并到Node.js中,但是提供了比EventEmitter更强大的功能,API也符合EventEmitter和Node.js的思想,可以在前端应用。

  这里的all方法是指监听profile、timeline和skin方法后,执行回调函数,并传入监听接收到的数据。

  最后介绍一个多事件协作的解决方案:JSCEX(https://github . com/Jeffrey Zhao/JSCEX)。Jscex通过运行时编译(或者必要时运行前编译)的思想,将同步思维的代码转换成最终的异步代码执行。写代码时可以用同步思维来写,可以享受同步思维的便捷编写和异步执行的高效性能。如果通过Jscex编写,它将采用以下形式:

  var data=$await(Task.whenAll({

  配置文件:api.getUser(用户名),

  时间轴:api.getTimeline(username ),

  skin: api.getSkin(username )

  //使用data.profile,data.timeline,data.skin。

  //TODO本节感谢Jscex作者@老赵(http://blog.zhaojie.me/)的指正和帮助。

  利用事件队列解决雪崩问题所谓雪崩问题,就是在缓存失效的情况下,大并发、高流量会同时涌入数据库,数据库无法同时承受如此大的查询请求,从而影响网站整体的缓慢响应。那么在Node.js中如何处理这种情况呢?

  var select=函数(回调){

  db.select(SQL ,函数(结果){

  回拨(成绩);

  };以上是一个数据库查询的调用。如果站点刚刚启动,此时缓存中没有数据。但是如果访问量很大,同样的SQL会被发送到数据库重复查询,会影响服务的整体性能。一个改进是添加一个状态锁。

  var status= ready

  var select=函数(回调){

  if(状态===就绪){

  状态=“挂起”;

  db.select(SQL ,函数(结果){

  回拨(成绩);

  状态=“就绪”;

  };但是,在这个场景中,select被重复调用,只有第一次调用有效,后续的select没有数据服务。所以此时引入事件队列:

  var proxy=new event proxy();

  var status= ready

  var select=函数(回调){

  proxy.once(selected ,回调);

  if(状态===就绪){

  状态=“挂起”;

  db.select(SQL ,函数(结果){

  proxy.emit(selected ,results);

  状态=“就绪”;

  };在这里,EventProxy对象的once方法用于将所有请求的回调推送到事件队列中,监视器在执行一次后将被移除的特性确保了每个回调只执行一次。对于同一个SQL语句,可以保证从同一查询的开始到结束总是只有一个调用。在这个查询期间,传入的调用只需要在队列中等待数据准备好,从而节省了重复的数据库调用开销。由于Node.js的单线程执行,这里不用担心状态。其实这种方法也可以应用到其他远程调用场景中,即使没有外部缓存策略,也能有效节省重复开销。这里也可以用EventEmitter代替EventProxy,但是可能会有太多的侦听器,这可能会导致警告。您需要调用setMaxListeners(0)来移除警告或设置更大的警告阈值。

  参考:3358 nodejs . org/docs/latest/API/events . html 3359 github . com/jacksonian/event proxy/blob/Master/README . MD 3359 github . com/Jeffrey Zhao/jscex/blob/Master/README-cn . MD关于作者,新浪微博@ Park Ling,前端工程师,曾就职于SAP,现就职于淘宝,花名Park Ling,致力于NodeJS和移动Web App的研发工作。双修前端JavaScript,希望把NodeJS介绍给更多的工程师。兴趣:读万卷书,行万里路。个人Github地址:http://github.com/JacksonTian.

node 事件,node的事件循环机制