Node.js事件的正确使用方法


Posted in Javascript onApril 05, 2019

前言

事件驱动的编程变得流行之前,在程序内部进行通信的标准方法非常简单:如果一个组件想要向另外一个发送消息,只是显式地调用了那个组件上的方法。但是在 react 中用的却是事件驱动而不是调用。

事件的好处

这种方法能够使组件更加分离。在我们继续写程序时,会识别整个过程中的事件,在正确的时间触发它们,并为每个事件附加一个或多个事件监听器,这使得功能扩展变得更加容易。我们可以为特定事件添加更多的 listener,而不必修改现有的侦听器或触发事件的应用程序部分。我们所谈论的是观察者模式。

Node.js事件的正确使用方法

设计一个事件驱动的体系结构

对事件进行识别非常重要,我们不希望最终必须从系统中删除或替换现有事件,因为这可能会迫使我们删除或修改附加到事件上的众多侦听器。我的一般原则是仅在业务逻辑单元完成执行时才考虑触发事件。

假如你想在用户注册后发送一堆不同的电子邮件。注册过程本身可能会涉及许多复杂的步骤和查询,但从商业角度来看,这只是其中的一个步骤。每个要发送的电子邮件也是单独的步骤。因此,一旦注册完成马上就发布事件是很有意义的。于是我们附加了多个监听器,每个监听器负责发送一种类型的电子邮件。

Node的异步事件驱动架构具有一些被称为“emitters”的对象。它们发出命名事件,这些事件会调用被称为“listener”的函数。发出事件的所有对象都是 EventEmitter 类的实例。使用它,我们可以创建自己的事件:

一个例子

让我们使用内置的 events 模块(我建议你查看这个文档:https://nodejs.org/api/events...)以获取对 EventEmitter 的访问权限。

const EventEmitter = require('events');

const myEmitter = new EventEmitter();

module.exports = myEmitter;

这是我们的服务器端程序的一部分,它负责接收HTTP请求,保存新用户并发出事件:

const myEmitter = require('./my_emitter');

// Perform the registration steps

// Pass the new user object as the message passed through by this event.
myEmitter.emit('user-registered', user);

附加一个监听器的单独模块:

const myEmitter = require('./my_emitter');

myEmitter.on('user-registered', (user) => {
 // Send an email or whatever.
});

将策略与实现分开是一种非常好的做法。在这种情况下,策略意味着哪些 listener 订阅了哪些事件。实现意味着 listener 自己。

const myEmitter = require('./my_emitter');
const sendEmailOnRegistration = require('./send_email_on_registration');
const someOtherListener = require('./some_other_listener');

myEmitter.on('user-registered', sendEmailOnRegistration);
myEmitter.on('user-registered', someOtherListener);
module.exports = (user) => {
 // Send a welcome email or whatever.
}

这种分离使 listener 也可以被重复使用,它可以被附加到发送相同消息的其他事件上(用户对象)。同样重要的是 当多个 listener 被附加到单个事件时,它们将按照附加的顺序同步执行。因此 someOtherListener 将在 sendEmailOnRegistration 完成执行后运行。

但是,如果你希望自己的 listener 以异步方式运行,只需用 setImmediate 包装它们的实现,如下所示:

module.exports = (user) => {
 setImmediate(() => {
 // Send a welcome email or whatever.
 });
}

让你的 Listeners 保持简洁

在写 listener 时要坚持单一责任原则。一个 listener 应该只做一件事并把事情做好。例如:要避免在 listener 中编写太多的条件并根据事件传来的数据(消息)去决定做什么。在这种情况下使用不同的事件会更加合适:

const myEmitter = require('./my_emitter');

// Perform the registration steps

// The application should react differently if the new user has been activated instantly.
if (user.activated) {
 myEmitter.emit('user-registered:activated', user);
 
} else {
 myEmitter.emit('user-registered', user);
}
const myEmitter = require('./my_emitter');
const sendEmailOnRegistration = require('./send_email_on_registration');
const someOtherListener = require('./some_other_listener');
const doSomethingEntirelyDifferent = require('./do_something_entirely_different');


myEmitter.on('user-registered', sendEmailOnRegistration);
myEmitter.on('user-registered', someOtherListener);

myEmitter.on('user-registered:activated', doSomethingEntirelyDifferent);
view raw

必要时明确分离 Listener

在前面的例子中,我们的 listener 是完全独立的函数。但是在 listener 与对象关联的情况下(这时是一种方法),必须手动将其从已订阅的事件中分离出来。否则对象将永远不会被垃圾回收,因为对象( listener )的一部分将会继续被外部对象( emitter )引用,所以存在内存泄漏的可能。

例如,如果我们正在开发一个聊天程序,并且希望当新消息到达用户进入的聊天室时,显示通知的功能应该位于该用户对象本身的内部,我们可能会这样做:

class ChatUser {
 
 displayNewMessageNotification(newMessage) {
 // Push an alert message or something.
 }
 
 // `chatroom` is an instance of EventEmitter.
 connectToChatroom(chatroom) {
 chatroom.on('message-received', this.displayNewMessageNotification);
 }

 disconnectFromChatroom(chatroom) {
 chatroom.removeListener('message-received', this.displayNewMessageNotification);
 }
}

当用户关闭他的标签或暂时断开互联网连接时,我们可能希望在服务器端发起一个回调,通知其他用户有人刚刚下线。当然在这时为脱机用户调用 displayNewMessageNotification 没有任何意义。除非我们删除它,否则它将继续被用于调用新消息。如果不这样做,除了不必要的调用之外,用户对象也会被永久地保留在内存中。因此在用户脱机时应该在服务器端回调中调用 disconnectFromChatroom。

注意事项

如果不小心,即便是松散耦合的事件驱动架构也会导致复杂性的增加,可能会导致在系统中跟踪依赖关系变得很困难。如果我们从侦听器内部发出事件,程序会特别容易出现这类问题。这可能会触发意外的事件链。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
深入探讨JavaScript、JQuery屏蔽网页鼠标右键菜单及禁止选择复制
Jun 10 Javascript
jquery实现简单的轮换出现效果实例
Jul 23 Javascript
javascript设计模式之对象工厂函数与构造函数详解
Jul 30 Javascript
简要了解jQuery移动web开发的响应式布局设计
Dec 04 Javascript
jQuery Form 表单提交插件之formSerialize,fieldSerialize,fieldValue,resetForm,clearForm,clearFields的应用
Jan 23 Javascript
老生常谈onBlur事件与onfocus事件(js)
Jul 09 Javascript
jquery实现瀑布流效果 jquery下拉加载新数据
Dec 12 Javascript
smartupload实现文件上传时获取表单数据(推荐)
Dec 12 Javascript
angular-ui-sortable实现可拖拽排序列表
Dec 28 Javascript
Vue之mixin全局的用法详解
Aug 22 Javascript
vue完成项目后,打包成静态文件的方法
Sep 03 Javascript
JavaScript实现简单计时器
Jun 22 Javascript
利用Node.js如何实现文件循环覆写
Apr 05 #Javascript
详解JavaScript栈内存与堆内存
Apr 04 #Javascript
jQuery中实现text()的方法
Apr 04 #jQuery
基于 jQuery 实现键盘事件监听控件
Apr 04 #jQuery
详解微信图片防盗链“此图片来自微信公众平台 未经允许不得引用”的解决方案
Apr 04 #Javascript
基于Vue+elementUI实现动态表单的校验功能(根据条件动态切换校验格式)
Apr 04 #Javascript
vue学习笔记五:在vue项目里面使用引入公共方法详解
Apr 04 #Javascript
You might like
PHP var_dump遍历对象属性的函数与应用代码
2010/06/04 PHP
基于PHP文件操作的详解
2013/06/05 PHP
解析php取整的几种方式
2013/06/25 PHP
php实现的双向队列类实例
2014/09/24 PHP
PHP调试的强悍利器之PHPDBG
2016/02/22 PHP
php自动提交表单的方法(基于fsockopen与curl)
2016/05/09 PHP
PHP实现下载远程图片保存到本地的方法
2017/06/19 PHP
Laravel项目中timeAgo字段语言转换的改善方法示例
2019/09/16 PHP
Laravel 解决composer相关操作提示php相关异常的问题
2019/10/23 PHP
JavaScript国旗变换效果代码
2008/08/13 Javascript
JavaScript 事件记录使用说明
2009/10/20 Javascript
百度Popup.js弹出框进化版 拖拽小框架发布 兼容IE6/7/8,Firefox,Chrome
2010/04/13 Javascript
表单元素与非表单元素刷新区别详细解析
2013/11/06 Javascript
JS中Json数据的处理和解析JSON数据的方法详解
2016/06/29 Javascript
javascript如何创建对象
2016/08/29 Javascript
JavaScript中setTimeout的那些事儿
2016/11/14 Javascript
html5 canvas 详细使用教程
2017/01/20 Javascript
vue axios 表单提交上传图片的实例
2018/03/16 Javascript
深入理解Angularjs 脏值检测
2018/10/12 Javascript
原生js+canvas实现下雪效果
2020/08/02 Javascript
Jquery 获取相同NAME 或者id删除行操作
2020/08/24 jQuery
[01:01:13]2018DOTA2亚洲邀请赛 4.5 淘汰赛 Mineski vs VG 第三场
2018/04/06 DOTA
Python正则表达式的使用范例详解
2014/08/08 Python
python回溯法实现数组全排列输出实例分析
2015/03/17 Python
python 连接sqlite及简单操作
2017/06/30 Python
Django中使用Whoosh进行全文检索的方法
2019/03/31 Python
使用Python的OpenCV模块识别滑动验证码的缺口(推荐)
2019/05/10 Python
Django框架ORM数据库操作实例详解
2019/11/07 Python
js正则匹配markdown里的图片标签的实现
2021/03/24 Javascript
报告会主持词
2014/04/02 职场文书
环保口号大全
2014/06/12 职场文书
2014公司年终工作总结
2014/12/19 职场文书
2016消防宣传标语口号
2015/12/26 职场文书
创业计划书之花店
2019/09/20 职场文书
适合后台管理系统开发的12个前端框架(小结)
2021/06/29 Javascript
Oracle数据库事务的开启与结束详解
2022/06/25 Oracle