浅谈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 相关文章推荐
[JS]点出统计器
Oct 11 Javascript
Ext 表单布局实例代码
Apr 30 Javascript
JavaScript字符串String和Array操作的有趣方法
Dec 18 Javascript
js实现文字跟随鼠标移动而移动的方法
Feb 28 Javascript
jQuery 出现Cannot read property ‘msie’ of undefined错误的解决方法
Nov 23 Javascript
ES6中Math对象新增的方法实例详解
Apr 25 Javascript
详解AngularJS用Interceptors来统一处理HTTP请求和响应
Jun 08 Javascript
bootstrap时间控件daterangepicker使用方法及各种小bug修复
Oct 25 Javascript
vue两个组件间值的传递或修改方式
Jul 04 Javascript
vue移动端微信授权登录插件封装的实例
Aug 28 Javascript
快速解决vue-cli在ie9+中无效的问题
Sep 04 Javascript
vue backtop组件的实现完整代码
Apr 07 Vue.js
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 Token(令牌)设计
2008/03/15 PHP
PHP中其实也可以用方法链
2011/11/10 PHP
使用php显示搜索引擎来的关键词
2014/02/13 PHP
PHP获取数组长度或某个值出现次数的方法
2015/02/11 PHP
PHP中spl_autoload_register()函数用法实例详解
2016/07/18 PHP
PHP使用栈解决约瑟夫环问题算法示例
2017/08/27 PHP
thinkphp3.2.0 setInc方法 源码全面解析
2018/01/29 PHP
PHP四种排序算法实现及效率分析【冒泡排序,插入排序,选择排序和快速排序】
2018/04/27 PHP
PHP利用缓存处理用户注册时的邮箱验证,成功后用户数据存入数据库操作示例
2019/12/31 PHP
原来Jquery.load的方法可以一直load下去
2011/03/28 Javascript
jquery对表单操作2
2011/04/06 Javascript
javascript 随机展示头像实现代码
2011/12/06 Javascript
javascript定义变量时加var与不加var的区别
2014/12/22 Javascript
对称加密与非对称加密优缺点详解
2017/02/06 Javascript
js 博客内容进度插件详解
2017/02/19 Javascript
微信小程序中input标签详解及简单实例
2017/05/18 Javascript
详解vuejs之v-for列表渲染
2017/06/22 Javascript
Node.js利用console输出日志文件的方法示例
2018/04/27 Javascript
jQuery实现数字自动增加或者减少的动画效果示例
2018/12/11 jQuery
解决node.js含有%百分号时发送get请求时浏览器地址自动编码的问题
2019/11/20 Javascript
Vue学习笔记之计算属性与侦听器用法
2019/12/07 Javascript
解决vue字符串换行问题(绝对管用)
2020/08/06 Javascript
解决vue数据不实时更新的问题(数据更改了,但数据不实时更新)
2020/10/27 Javascript
在VS Code上搭建Python开发环境的方法
2018/04/06 Python
python使用turtle库绘制时钟
2020/03/25 Python
Python+OpenCV实现旋转文本校正方式
2020/01/09 Python
TensorFlow tensor的拼接实例
2020/01/19 Python
Windows 下python3.8环境安装教程图文详解
2020/03/11 Python
Pycharm 解决自动格式化冲突的设置操作
2021/01/15 Python
CSS3 media queries + jQuery实现响应式导航
2016/09/30 HTML / CSS
英国豪华装饰照明品牌的在线零售商:Inspyer Lighting
2019/12/10 全球购物
市场营销专业毕业生自荐信
2013/11/02 职场文书
物业管理专业个人的自我评价
2013/11/19 职场文书
2014党员学习习主席讲话思想汇报
2014/09/15 职场文书
2019大学生社会实践报告汇总
2019/08/16 职场文书
再也不用花钱买漫画!Python爬取某漫画的脚本及源码
2021/06/09 Python