浅谈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 解决Ajax请求的页面 浏览器后退前进功能,页面刷新功能实效问题
Dec 11 Javascript
js在输入框屏蔽按键,只能键入数字的示例代码
Jan 03 Javascript
基于JQuery制作可编辑的表格特效
Dec 23 Javascript
jquery获取节点名称
Apr 26 Javascript
javascript表单处理具体实现代码(表单、链接、按钮)
May 07 Javascript
微信小程序 http请求详细介绍
Oct 09 Javascript
JavaScript简单拖拽效果(1)
May 17 Javascript
JavaScript笛卡尔积超简单实现算法示例
Jul 30 Javascript
浅谈Node框架接入ELK实践总结
Feb 22 Javascript
使用vue-cli3 创建vue项目并配置VS Code 自动代码格式化 vue语法高亮问题
May 14 Javascript
react 移动端实现列表左滑删除的示例代码
Jul 04 Javascript
vue+animation实现翻页动画
Jun 29 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文件上传你必须知道的几点
2015/10/20 PHP
php5.3/5.4/5.5/5.6/7常见新增特性汇总整理
2020/02/27 PHP
PHP safe_mode开启对于PHP系统函数有什么影响
2020/11/10 PHP
jQuery 1.0.4 - New Wave Javascript(js源文件)
2007/01/15 Javascript
自动生成文章摘要的代码[JavaScript 版本]
2007/03/20 Javascript
Js 订制自己的AlertBox(信息提示框)
2009/01/09 Javascript
javascript 多浏览器 事件大全
2010/03/23 Javascript
使用jquery实现图文切换效果另加特效
2013/01/20 Javascript
基于javascipt-dom编程 table对象的使用
2013/04/22 Javascript
jquery实现手风琴效果实例代码
2013/11/15 Javascript
javascript中字符串拼接详解
2014/09/26 Javascript
Javascript中innerHTML用法实例分析
2015/01/12 Javascript
jquery任意位置浮动固定层插件用法实例
2015/05/29 Javascript
jquery validate demo 基础
2015/10/29 Javascript
JS中setTimeout和setInterval的最大延时值详解
2017/02/13 Javascript
使用vue官方提供的模板vue-cli搭建一个helloWorld案例分析
2018/01/16 Javascript
TypeScript基础入门教程之三重斜线指令详解
2018/10/22 Javascript
vue项目搭建以及全家桶的使用详细教程(小结)
2018/12/19 Javascript
js实现简单的贪吃蛇游戏
2020/04/23 Javascript
[49:21]2018DOTA2亚洲邀请赛3月30日 小组赛B组 Effect VS iG
2018/03/31 DOTA
使用python实现ANN
2017/12/20 Python
Python使用POP3和SMTP协议收发邮件的示例代码
2019/04/16 Python
基于Python实现剪切板实时监控方法解析
2019/09/11 Python
Python+OpenCV 实现图片无损旋转90°且无黑边
2019/12/12 Python
Python While循环语句实例演示及原理解析
2020/01/03 Python
个性化皮包、小袋、生活配件:Mon Purse
2019/03/26 全球购物
Zalando Lounge瑞士:时尚与生活方式购物俱乐部
2020/03/12 全球购物
shallow copy和deep copy的区别
2016/05/09 面试题
客户代表自我评价范例
2013/09/24 职场文书
教师四风自我剖析材料
2014/09/30 职场文书
药店采购员岗位职责
2014/09/30 职场文书
开幕式邀请函
2015/01/31 职场文书
行政处罚事先告知书
2015/07/01 职场文书
《云雀的心愿》教学反思
2016/02/23 职场文书
python实现简易名片管理系统
2021/04/11 Python
豆瓣2021评分最高动画剧集-豆瓣评分最高的动画剧集2021
2022/03/18 日漫