浅谈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 相关文章推荐
基于javascript html5实现3D翻书特效
Mar 14 Javascript
聊一聊JavaScript作用域和作用域链
May 03 Javascript
Javascript基础知识盲点总结之函数
May 15 Javascript
拥Bootstrap入怀——导航栏篇
May 30 Javascript
原生js轮播特效
May 18 Javascript
浅谈vue单一组件下动态修改数据时的全部重渲染
Mar 01 Javascript
JS实现Cookie读、写、删除操作工具类示例
Aug 28 Javascript
vue实现打印功能的两种方法
Sep 07 Javascript
Vue.js上传图片到阿里云OSS存储的方法示例
Dec 13 Javascript
JavaScript变速动画函数封装添加任意多个属性
Apr 03 Javascript
vue实现鼠标移过出现下拉二级菜单功能
Dec 12 Javascript
vue实现分页加载效果
Dec 24 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
成本8450万,票房仅2亿,口碑两极分化,又一部DC电影扑街了
2020/04/09 欧美动漫
php基于str_pad实现卡号不足位数自动补0的方法
2014/11/12 PHP
js getElementsByTagName的简写方式
2010/06/27 Javascript
浅谈javascript的调试
2015/01/28 Javascript
js实现新年倒计时效果
2015/12/10 Javascript
浅析location.href跨窗口调用函数
2016/11/22 Javascript
JavaScript的数据类型转换原则(干货)
2018/03/15 Javascript
深入浅析nuxt.js基于ssh的vue通用框架
2019/05/21 Javascript
关于layui flow loading占位图的实现方法
2019/09/21 Javascript
在layer弹层layer.prompt中,修改placeholder的实现方法
2019/09/27 Javascript
jQuery HTML设置内容和属性操作实例分析
2020/05/20 jQuery
[03:06]2018年度CS GO最具人气解说-完美盛典
2018/12/16 DOTA
python通过imaplib模块读取gmail里邮件的方法
2015/05/08 Python
Python使用plotly绘制数据图表的方法
2017/07/18 Python
python Tkinter的图片刷新实例
2019/06/14 Python
Python爬取知乎图片代码实现解析
2019/09/17 Python
Python定时从Mysql提取数据存入Redis的实现
2020/05/03 Python
python pymysql链接数据库查询结果转为Dataframe实例
2020/06/05 Python
keras.layer.input()用法说明
2020/06/16 Python
详解python第三方库的安装、PyInstaller库、random库
2021/03/03 Python
css3圆角边框和边框阴影示例
2014/05/05 HTML / CSS
CSS3使用transition实现的鼠标悬停淡入淡出
2015/01/09 HTML / CSS
德国领先的大尺码和超大尺码男装在线零售商:Bigtex
2019/06/22 全球购物
英国百年闻名的优质健康产品连锁店:Holland & Barrett
2019/12/19 全球购物
Python文件操作的面试题
2013/06/22 面试题
农药学硕士毕业生自荐信
2013/09/25 职场文书
工程师岗位职责
2013/11/08 职场文书
保洁主管岗位职责
2013/11/20 职场文书
房产转让协议书
2014/04/11 职场文书
辞旧迎新演讲稿
2014/09/15 职场文书
大学生党员个人剖析材料
2014/10/08 职场文书
教师党的群众路线学习心得体会
2014/11/04 职场文书
工作自我推荐信范文
2015/03/25 职场文书
政府会议通知范文
2015/04/15 职场文书
罚款通知怎么写
2015/04/22 职场文书
Java+swing实现抖音上的表白程序详解
2022/06/25 Java/Android