浅谈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 和 CSS 的文本特效插件集锦
Dec 12 Javascript
jquery实现从数组移除指定的值
Jun 24 Javascript
原生js实现jquery函数animate()动画效果的简单实例
Aug 21 Javascript
JS实用的带停顿的逐行文本循环滚动效果实例
Nov 23 Javascript
JavaScript数组push方法使用注意事项
Oct 30 Javascript
JS使用遮罩实现点击某区域以外时弹窗的弹出与关闭功能示例
Jul 31 Javascript
JavaScript基础教程之如何实现一个简单的promise
Sep 11 Javascript
详解js获取video任意时间的画面截图
Apr 17 Javascript
vue 组件中使用 transition 和 transition-group实现过渡动画
Jul 09 Javascript
webpack的pitching loader详解
Sep 23 Javascript
原生js拖拽实现图形伸缩效果
Feb 10 Javascript
Node.js API详解之 readline模块用法详解
May 22 Javascript
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
php计算程序运行时间的简单例子分享
2014/05/10 PHP
php的PDO事务处理机制实例分析
2017/02/16 PHP
Jquery知识点三 jquery表单对象操作
2011/01/17 Javascript
jquery仿京东导航/仿淘宝商城左侧分类导航下拉菜单效果
2013/04/24 Javascript
javascript检测页面是否缩放的小例子
2013/05/16 Javascript
javascript中普通函数的使用介绍
2013/12/19 Javascript
鼠标悬浮停留三秒后自动显示大图js代码
2014/09/09 Javascript
浅析Bootstrap组件之面板组件
2016/05/04 Javascript
JavaWeb表单及时验证功能在输入后立即验证(含用户类型,性别,爱好...的验证)
2017/06/09 Javascript
使用axios实现上传图片进度条功能
2017/12/21 Javascript
vue项目如何刷新当前页面的方法
2018/05/18 Javascript
JavaScript控制浏览器全屏显示简单示例
2018/07/05 Javascript
浅谈Vue CLI 3结合Lerna进行UI框架设计
2019/04/14 Javascript
jquery分页优化操作实例分析
2019/08/23 jQuery
微信分享invalid signature签名错误踩过的坑
2020/04/11 Javascript
JavaScript实现复选框全选和取消全选
2020/11/20 Javascript
python发送伪造的arp请求
2014/01/09 Python
python中getattr函数使用方法 getattr实现工厂模式
2014/01/20 Python
Python中实现字符串类型与字典类型相互转换的方法
2014/08/18 Python
Python实现Linux下守护进程的编写方法
2014/08/22 Python
Python fileinput模块使用实例
2015/06/03 Python
pycharm 将django中多个app放到同个文件夹apps的处理方法
2018/05/30 Python
详解django自定义中间件处理
2018/11/21 Python
django之从html页面表单获取输入的数据实例
2020/03/16 Python
python的Jenkins接口调用方式
2020/05/12 Python
简述 Python 的类和对象
2020/08/21 Python
纯CSS3打造动感漂亮时尚的扇形菜单
2014/03/18 HTML / CSS
用CSS3写的模仿iPhone中的返回按钮
2015/04/04 HTML / CSS
详解CSS3的图层阴影和文字阴影效果使用
2016/06/09 HTML / CSS
css3实现椭圆轨迹旋转的示例代码
2018/10/29 HTML / CSS
戴尔英国翻新电脑和电子产品:Dell UK Refurbished Computers
2019/07/30 全球购物
爽歪歪广告词
2014/03/20 职场文书
实习协议书范本
2014/04/22 职场文书
作文评语集锦大全
2014/04/23 职场文书
100句人生哲理语录集锦:强者征服今天,懒汉坐等明天
2019/10/18 职场文书
Spring Boot mybatis-config 和 log4j 输出sql 日志的方式
2021/07/26 Java/Android