浅谈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 tools 系列 scrollable学习
Sep 06 Javascript
javascript之通用简单的table选项卡实现(二)
May 09 Javascript
js怎么终止程序return不行换jfslk
May 30 Javascript
JavaScript实现页面实时显示当前时间的简单实例
Jul 20 Javascript
js用typeof方法判断undefined类型
Jul 15 Javascript
javascript实现禁止右键和F12查看源代码
Dec 26 Javascript
JavaScript实现点击文字切换登录窗口的方法
May 11 Javascript
javascript实现tab切换特效
Nov 12 Javascript
javascript 判断是否是微信浏览器的方法
Oct 09 Javascript
在JS中a标签加入单击事件屏蔽href跳转页面
Dec 16 Javascript
WebSocket的简单介绍及应用
May 23 Javascript
layui 数据表格 点击分页按钮 监听事件的实例
Sep 02 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 模拟$_PUT实现代码
2010/03/15 PHP
PHP针对常规模板引擎中与CSS/JSON冲突的解决方法
2014/08/19 PHP
PHP中使用sleep造成mysql读取失败的案例和解决方法
2014/08/21 PHP
Javascript 函数中的参数使用分析
2010/03/27 Javascript
jquery tab标签页的制作
2010/05/10 Javascript
浅析JavaScript中的隐式类型转换
2013/12/05 Javascript
JavaScript Sort 的一个错误用法示例
2015/03/20 Javascript
Javascript闭包(Closure)详解
2015/05/05 Javascript
js实现商品抛物线加入购物车特效
2020/11/18 Javascript
jQuery Easyui Tabs扩展根据自定义属性打开页签
2016/08/15 Javascript
bootstrap按钮插件(Button)使用方法解析
2017/01/13 Javascript
JavaScript调试之console.log调试的一个小技巧分享
2017/08/07 Javascript
在vue项目中引入highcharts图表的方法
2019/01/21 Javascript
手挽手带你学React之React-router4.x的使用
2019/02/14 Javascript
详解Vue路由自动注入实践
2019/04/17 Javascript
JQuery特殊效果和链式调用操作示例
2019/05/13 jQuery
详解element-ui级联菜单(城市三级联动菜单)和回显问题
2019/10/02 Javascript
基于JS实现简单滑块拼图游戏
2019/10/12 Javascript
ES6箭头函数和扩展实例分析
2020/05/23 Javascript
python编写爬虫小程序
2015/05/14 Python
浅析python的Lambda表达式
2019/02/27 Python
查看python安装路径及pip安装的包列表及路径
2019/04/03 Python
使用pandas的box_plot去除异常值
2019/12/10 Python
通过 Python 和 OpenCV 实现目标数量监控
2020/01/05 Python
英国最大的在线运动补充剂商店:Discount Supplements
2017/06/03 全球购物
美国尼曼百货官网:Neiman Marcus
2019/09/05 全球购物
土木工程毕业生推荐信
2013/10/28 职场文书
电子商务专业学生的自我鉴定
2013/11/28 职场文书
护理助产毕业生的求职信
2014/03/02 职场文书
护士毕业实习感言
2014/03/05 职场文书
销售员态度差检讨书
2014/10/26 职场文书
python tkinter模块的简单使用
2021/04/07 Python
Redis如何一键部署脚本
2021/04/12 Redis
python基础详解之if循环语句
2021/04/24 Python
原生Javascript+HTML5一步步实现拖拽排序
2021/06/12 Javascript
详解Spring Security中的HttpBasic登录验证模式
2022/03/17 Java/Android