浅谈node的事件机制


Posted in Javascript onOctober 09, 2017

Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.

在nodejs的官方文档中,明确写出了node的一个特性是event-driven(事件驱动),可见其非常重要。查看源码,我们可知其事件机制为用js写的EventEmitter类,写得非常优雅且应用了发布/订阅模式。

通过实现一个简易的、具有发布/订阅模式的事件机制,以此来理清EventEmitter类的实现思路

Publish/Subscribe(发布/订阅模式)

类比

要讲的是一个模式,模式这个词,听起来就很抽象。我们先举个栗子。假设有一家报纸机构,提供晨报、午报、晚报。如果你想要看某种报纸,你需要向报纸机构订阅,等对应的报纸发布出来后,报纸机构就会通知你来拿报纸。

在这个过程中,报纸机构实现了两个功能,一是接受客户的订阅;二是发布不同类型的报纸。发布报纸的时候,订阅该类型报纸的客户就能接收到通知。

这个报纸机构也就是我们要实现的事件机制。

浅谈node的事件机制

目的

从上面的例子可以看出:1.发布报纸;2.将报纸给到客户;这个连续的过程由于报纸机构的存在,变成了可以先订阅,再发布,等到发布就自动送到客户手中,实现了动作时间上的分离。这也是发布/订阅系统的优势。

实现思路

我们有3种报纸,对应3个事件,每个事件发生时要通知客户。对应的数据格式可以如下:

var Event = {
 morning: event1,
 noon: event2,
 night: event3
}

由于每种报纸都可能有不止一个人订阅,那么格式可优化成这样:

var Event = {
 morning: [e11, e12,...],
 noon: [e21, e22],
 night: event3
}

当用户订阅的时候,我们就将其事件添加对应的数组中;当事件发布的时候,就执行相应事件。说白了就是先存储后使用。

具体代码如下:

1.on表示订阅,将事件添加到对应数组中
2.emit表示发布,将对应数组中的数据取出来执行
3.off表示删除无用的事件

var Event = {
 on: function(key, listener) {
  if (!this.__events) {
   this.__events = {}
  }
  if (!this.__events[key]) {
   this.__events[key] = [];
  } 
  if (_indexOf(this.__events[key], listener) == -1 && typeof listener === 'function') {
   this.__events[key].push(listener)
  }
 },
 emit: function(key) {
  if (!this.__events || !this.__events[key]) return 
  //取得每次订阅不同的参数
  var arg = Array.prototype.slice.call(arguments, 1) || [];

  var listeners = this.__events[key];
  var len = listeners.length;

  for (var i=0; i<len; i++) {
   listeners[i].apply(this, arg)
  }
  return this

 },
 off: function(key, listener) {
  if (!key && !listener) {
   this.__events = {}
  }
  if (key && !listener) {
   delete this.__events[key]
  }
  if (key && listener) {
   var listeners = this.__events[key];
   var index = _indexOf(listeners, listener);
   (index > -1) && listeners.splice(index, 1);
  }
  return this
 }
}

var _indexOf = function(array,key){
 if (array === null) return -1
 var i = 0, length = array.length
 for (; i < length; i++) if (array[i] === key) return i
 return -1
}
//调用
Event.on('console1', function(num) {
 console.log(num); // 1
});

Event.emit('console1', 1)

node的EventEmitter

node的EventEmitter基本逻辑和上面提供的例子基本一样,只是更加复杂些。

1.订阅事件on

function _addListener(target, type, listener, prepend) {
 var m;
 var events;
 var existing;

 if (typeof listener !== 'function')
  throw new TypeError('"listener" argument must be a function');

 events = target._events;
 ...
 if (typeof existing === 'function') {
   // Adding the second element, need to change to array.
   existing = events[type] =
    prepend ? [listener, existing] : [existing, listener];
  } else {
   // If we've already got an array, just append.
   if (prepend) {
    existing.unshift(listener);
   } else {
    existing.push(listener);
   }
  }

 return target;
}

EventEmitter.prototype.addListener = function addListener(type, listener) {
 return _addListener(this, type, listener, false);
};

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

2.发布事件

EventEmitter.prototype.emit = function emit(type) {
 ...
 handler = events[type];
 switch (len) {
  // fast cases
  case 1:
   emitNone(handler, isFn, this);
   break;
  case 2:
   emitOne(handler, isFn, this, arguments[1]);
   break;
  case 3:
   emitTwo(handler, isFn, this, arguments[1], arguments[2]);
   break;
  case 4:
   emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
   break;
  // slower
  default:
   args = new Array(len - 1);
   for (i = 1; i < len; i++)
    args[i - 1] = arguments[i];
   emitMany(handler, isFn, this, args);
 }
}

讲到这里,相信大家已经明白EventEmitter的实现思路。

参考资料

node events.js

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery调用wcf并展示出数据的方法
Jul 07 Javascript
JavaScript中的值是按值传递还是按引用传递问题探讨
Jan 30 Javascript
moment.js轻松实现获取当前日期是当年的第几周
Feb 05 Javascript
javascript去掉代码里面的注释
Jul 24 Javascript
jfinal与bootstrap的登录跳转实战演习
Sep 22 Javascript
深入理解jQuery之事件移除
Jun 02 Javascript
基于JSON格式数据的简单jQuery幻灯片插件(jquery-slider)
Aug 10 Javascript
详解node+express+ejs+bootstrap构建项目
Sep 27 Javascript
微信小程序实现页面跳转传值以及获取值的方法分析
Dec 18 Javascript
Vue自定义toast组件的实例代码
Aug 15 Javascript
vue路由切换时取消之前的所有请求操作
Sep 01 Javascript
如何在vue 中引入使用jquery
Nov 10 jQuery
JS实现中文汉字按拼音排序的方法
Oct 09 #Javascript
ES6中的Promise代码详解
Oct 09 #Javascript
js+html5生成自动排列对话框实例
Oct 09 #Javascript
jQuery实现获取table中鼠标click点击位置行号与列号的方法
Oct 09 #jQuery
详解如何让Express支持async/await
Oct 09 #Javascript
jQuery插件artDialog.js使用与关闭方法示例
Oct 09 #jQuery
Node.JS使用Sequelize操作MySQL的示例代码
Oct 09 #Javascript
You might like
discuz安全提问算法
2007/06/06 PHP
PHP脚本监控Nginx 502错误并自动重启php-fpm
2015/05/13 PHP
php基于dom实现的图书xml格式数据示例
2017/02/03 PHP
浅谈php常用的7大框架的优缺点
2020/07/20 PHP
Centos7.7 64位利用本地完整安装包安装lnmp/lamp套件教程
2021/03/09 Servers
非常不错的一个javascript 类
2006/11/07 Javascript
JS获取dom 对象 ajax操作 读写cookie函数
2009/11/18 Javascript
window.location.hash 属性使用说明
2010/03/20 Javascript
jquery动态分页效果堪比时光网
2014/09/25 Javascript
javascript动态修改Li节点值的方法
2015/01/20 Javascript
jquery实现全选功能效果的实现代码
2016/05/05 Javascript
深入理解jquery中的事件与动画
2016/05/24 Javascript
原生js实现自由拖拽弹窗代码demo
2016/06/29 Javascript
完全深入学习Bootstrap表单
2016/11/28 Javascript
[js高手之路]设计模式系列课程-发布者,订阅者重构购物车的实例
2017/08/29 Javascript
在vscode中统一vue编码风格的方法
2018/02/22 Javascript
jQuery实现鼠标滑过商品小图片上显示对应大图片功能【测试可用】
2018/04/27 jQuery
JavaScript面向对象程序设计中对象的定义和继承详解
2019/07/29 Javascript
es6函数之rest参数用法实例分析
2020/04/18 Javascript
[02:44]DOTA2英雄基础教程 魅惑魔女
2014/01/07 DOTA
Python中dictionary items()系列函数的用法实例
2014/08/21 Python
Python实现的检测网站挂马程序
2014/11/30 Python
python中(str,list,tuple)基础知识汇总
2018/02/20 Python
Python实现通讯录功能
2018/02/22 Python
Python中的异常处理try/except/finally/raise用法分析
2019/02/28 Python
Python叠加矩形框图层2种方法及效果
2020/06/18 Python
Electric官网:美国高级眼镜和配件品牌
2020/06/04 全球购物
儿科护理实习自我鉴定
2013/09/19 职场文书
护士自荐信
2013/10/25 职场文书
新娘父亲婚礼致辞
2014/01/16 职场文书
企业标语口号
2014/06/10 职场文书
2014医学院领导班子对照检查材料思想汇报
2014/09/19 职场文书
篮球友谊赛通讯稿
2014/10/10 职场文书
诉讼代理人授权委托书
2014/10/11 职场文书
2014年政协委员工作总结
2014/12/01 职场文书
创业计划书之零食店(进口)
2019/09/24 职场文书