浅谈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下判断一个对象是否具有指定名称的属性的的代码
Jan 11 Javascript
如何让页面加载完成后执行js
Jun 26 Javascript
jquery高级编程的最佳实践详解
Mar 23 Javascript
轻松学习Javascript闭包函数
Dec 15 Javascript
sencha ext js 6 快速入门(必看)
Jun 01 Javascript
jQuery基于函数重载实现自定义Alert函数样式的方法
Jul 27 Javascript
微信小程序中使用javascript 回调函数
May 11 Javascript
微信小程序城市定位的实现实例(获取当前所在国家城市信息)
May 17 Javascript
JavaScript canvas仿代码流瀑布
Feb 10 Javascript
完美解决vue 中多个echarts图表自适应的问题
Jul 19 Javascript
JavaScript读取本地文件常用方法流程解析
Oct 12 Javascript
VUE前端从后台请求过来的数据进行转换数据结构操作
Nov 11 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 替换模板变量实现步骤
2009/08/24 PHP
PHP中如何调用webservice的实例参考
2013/04/25 PHP
浅析PHP中strlen和mb_strlen的区别
2014/08/31 PHP
php实现excel中rank函数功能的方法
2015/01/20 PHP
PHP7新增运算符用法实例分析
2016/09/26 PHP
Yii2 如何在modules中添加验证码的方法
2017/06/19 PHP
Symfony查询方法实例小结
2017/06/28 PHP
PHP读取CSV大文件导入数据库的实例
2017/07/24 PHP
向fckeditor编辑器插入指定代码的方法
2007/05/25 Javascript
JavaScript学习笔记(十)
2010/01/17 Javascript
在Firefox下js select标签点击无法弹出
2014/03/06 Javascript
分享一款基于jQuery的视频播放插件
2014/10/09 Javascript
jquery实现兼容IE8的异步上传文件
2015/06/15 Javascript
JS数组操作(数组增加、删除、翻转、转字符串、取索引、截取(切片)slice、剪接splice、数组合并)
2016/05/20 Javascript
js图片切换具体实现代码
2016/10/13 Javascript
Bootstrap基本组件学习笔记之导航(10)
2016/12/07 Javascript
js实现刷新页面后回到记录时滚动条的位置【两种方案可选】
2016/12/12 Javascript
jQuery中on方法使用注意事项详解
2017/02/15 Javascript
详解Angular如何正确的操作DOM
2018/07/06 Javascript
vue 项目build错误异常的解决方法
2019/04/22 Javascript
JavaScript中将值转换为字符串的五种方法总结
2019/06/06 Javascript
python多线程操作实例
2014/11/21 Python
实例介绍Python中整型
2019/02/11 Python
python GUI库图形界面开发之PyQt5菜单栏控件QMenuBar的详细使用方法与实例
2020/02/28 Python
Django websocket原理及功能实现代码
2020/11/14 Python
伦敦一家西班牙童装精品店:La Coqueta
2018/02/02 全球购物
澳大利亚设计的婴儿和女孩的衣服:Oobi
2018/12/16 全球购物
中职应届生会计求职信
2013/10/23 职场文书
文明礼仪小标兵事迹
2014/01/12 职场文书
给儿子的表扬信
2014/01/15 职场文书
婚前协议书怎么写
2014/04/15 职场文书
学校周年庆活动方案
2014/08/22 职场文书
学习党的群众路线对照检查材料
2014/09/29 职场文书
2015小学语文教师个人工作总结
2015/05/20 职场文书
2016年五四青年节校园广播稿
2015/12/17 职场文书
Java Spring Lifecycle的使用
2022/05/06 Java/Android