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异步编程:异步数据收集的具体方法
Aug 19 Javascript
JavaScript获取鼠标移动时的坐标(兼容IE8、chome谷歌、Firefox)
Sep 13 Javascript
JavaScript里实用的原生API汇总
May 14 Javascript
使用AmplifyJS组件配合JavaScript进行编程的指南
Jul 28 Javascript
jquery插件uploadify多图上传功能实现代码
Aug 12 Javascript
基于JavaScript实现鼠标向下滑动加载div的代码
Aug 31 Javascript
使用Angular.js开发的注意事项
Oct 19 Javascript
基于JS实现bookstore静态页面的实例代码
Feb 22 Javascript
js如何编写简单的ajax方法库
Aug 02 Javascript
vue 点击展开显示更多(点击收起部分隐藏)
Apr 09 Javascript
微信小程序页面滚动到指定位置代码实例
Sep 07 Javascript
vue v-on:click传递动态参数的步骤
Sep 11 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数字每三位加逗号的功能函数
2015/10/22 PHP
PHP实现微信模拟登陆并给用户发送消息的方法【文字,图片,图文】
2017/06/29 PHP
PHP正则表达式处理函数(PCRE 函数)实例小结
2019/05/09 PHP
实现png图片和png背景透明(支持多浏览器)的方法
2009/09/08 Javascript
js简单实现让文本框内容逐个字的显示出来
2013/10/22 Javascript
IE8下String的Trim()方法失效的解决方法
2013/11/08 Javascript
javascript框架设计读书笔记之数组的扩展与修复
2014/12/02 Javascript
jQuery控制Div拖拽效果完整实例分析
2015/04/15 Javascript
jQuery蓝色风格滑动导航栏代码分享
2015/08/19 Javascript
jQuery EasyUI Layout实现tabs标签的实例
2017/09/26 jQuery
angularjs实现天气预报功能
2020/06/16 Javascript
JavaScript Date对象应用实例分享
2017/10/30 Javascript
vue-cli3 从搭建到优化的详细步骤
2019/01/20 Javascript
js单线程的本质 Event Loop解析
2019/10/29 Javascript
js数据类型转换与流程控制操作实例分析
2019/12/18 Javascript
[45:52]2018DOTA2亚洲邀请赛 4.1小组赛 A组加赛 LGD vs Liquid
2018/04/02 DOTA
用Python进行TCP网络编程的教程
2015/04/29 Python
wxPython使用系统剪切板的方法
2015/06/16 Python
Python实现MySQL操作的方法小结【安装,连接,增删改查等】
2017/07/12 Python
python中如何使用正则表达式的非贪婪模式示例
2017/10/09 Python
python3爬虫怎样构建请求header
2018/12/23 Python
使用Python自动生成HTML的方法示例
2019/08/06 Python
python并发爬虫实用工具tomorrow实用解析
2019/09/25 Python
python实现超市商品销售管理系统
2019/11/22 Python
使用K.function()调试keras操作
2020/06/17 Python
python中的unittest框架实例详解
2021/02/05 Python
python lambda的使用详解
2021/02/26 Python
AE美国鹰日本官方网站: American Eagle Outfitters
2016/12/10 全球购物
explicit和implicit的含义
2012/11/15 面试题
高中英语教学反思
2014/02/04 职场文书
美容院经理岗位职责
2014/04/03 职场文书
市级绿色学校申报材料
2014/08/25 职场文书
公安领导班子四风问题个人整改措施思想汇报
2014/10/09 职场文书
工作推荐信模板
2015/03/25 职场文书
《一面五星红旗》教学反思
2016/02/23 职场文书
SpringBoot项目多数据源及mybatis 驼峰失效的问题解决方法
2022/07/07 Java/Android