浅谈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 相关文章推荐
根据IP的地址,区分不同的地区,查看不同的网站页面的js代码
Feb 26 Javascript
控制文字内容的显示与隐藏示例
Jun 11 Javascript
原生js和jQuery随意改变div属性style的名称和值
Oct 22 Javascript
在HTML中插入JavaScript代码的示例
Jun 03 Javascript
JS实现选中当前菜单后高亮显示的导航条效果
Oct 15 Javascript
javascript实现的上下无缝滚动效果
Sep 19 Javascript
jQuery的Cookie封装,与PHP交互的简单实现
Oct 05 Javascript
JS实现加载时锁定HTML页面元素的方法
Jun 24 Javascript
浅谈JS对html标签的属性的干预以及对CSS样式表属性的干预
Jun 25 Javascript
jQuery动态添加.active 实现导航效果代码思路详解
Aug 29 jQuery
jQuery中的for循环var与let的区别
Apr 21 jQuery
ES6 中可以提升幸福度的小功能
Aug 06 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自动跳转中英文页面
2008/07/29 PHP
phpmyadmin导入(import)文件限制的解决办法
2009/12/11 PHP
Views rows style模板重写代码
2011/05/16 PHP
PHP根据IP判断地区名信息的示例代码
2014/03/03 PHP
制作安全性高的PHP网站的几个实用要点
2014/12/30 PHP
Thinkphp 中 distinct 的用法解析
2016/12/14 PHP
PHP中OpenSSL加密问题整理
2017/12/14 PHP
JQuery datepicker 使用方法
2011/05/20 Javascript
DOM基础教程之事件类型
2015/01/20 Javascript
JavaScript弹出新窗口并控制窗口移动到指定位置的方法
2015/04/06 Javascript
jQuery通用的全局遍历方法$.each()用法实例
2016/07/04 Javascript
AngularJS延迟加载html template
2016/07/27 Javascript
ES6记录异步函数的执行时间详解
2016/08/31 Javascript
Angular表单验证实例详解
2016/10/20 Javascript
nodejs中向HTTP响应传送进程的输出
2017/03/19 NodeJs
javascript 的变量、作用域和内存问题
2017/04/19 Javascript
jQuery模拟爆炸倒计时功能实例代码
2017/08/21 jQuery
在vue中使用css modules替代scroped的方法
2018/03/10 Javascript
vue动态绑定组件子父组件多表单验证功能的实现代码
2018/05/14 Javascript
jQuery判断自定义属性data-val用法示例
2019/01/07 jQuery
Vue+Element UI+Lumen实现通用表格分页功能
2019/02/02 Javascript
[51:06]2018DOTA2亚洲邀请赛3月29日 小组赛A组 KG VS Liquid
2018/03/30 DOTA
python 删除大文件中的某一行(最有效率的方法)
2017/08/19 Python
浅析Python装饰器以及装饰器模式
2018/05/28 Python
Python迭代器与生成器基本用法分析
2018/07/26 Python
Tensorflow 实现修改张量特定元素的值方法
2018/07/30 Python
Django框架搭建的简易图书信息网站案例
2019/05/25 Python
解决Atom安装Hydrogen无法运行python3的问题
2019/08/28 Python
python实现图片上添加图片
2019/11/26 Python
基于Tensorflow高阶读写教程
2020/02/10 Python
Pytorch使用PIL和Numpy将单张图片转为Pytorch张量方式
2020/05/25 Python
Python 爬取淘宝商品信息栏目的实现
2021/02/06 Python
CSS3中Transition动画属性用法详解
2016/07/04 HTML / CSS
美国专业汽车音响和移动电子产品零售商:Car Toys
2019/05/13 全球购物
大学生求职意向书
2015/05/11 职场文书
2015年卫生院健康教育工作总结
2015/07/24 职场文书