专栏首页京程一灯JS 和 Node.js 中的“事件驱动”是什么意思?[每日前端夜话0x106]

JS 和 Node.js 中的“事件驱动”是什么意思?[每日前端夜话0x106]

翻译:疯狂的技术宅

作者:Valentino Gagliardi

来源:valentinog.com

事件驱动和发布-订阅

事件驱动架构是建立在软件开发中一种通用模式上的,这种模式被称为发布-订阅观察者模式。

事件驱动架构中,至少有两个参与者:主题(subject)观察者(observer)

主题就像调频收音机一样,向有兴趣收听该主题所说内容的观察者进行广播

What means "event-driven" in JavaScript and Node.js?

观察者可能只有一个,也可能有一百个,这都没有关系,只要主题有一些要广播的消息就够了。

请记住,事件驱动、发布-订阅和观察者模式在实践中不是一回事,但在理想情况下,它们使用相同的方法:一个实体广播一条消息,其他实体侦听该消息。

发布-订阅模式和我一样老。在 1987 年左右开始理论化,而观察者模式则出现在 1994 年由“四人帮”所写的著作《设计模式》中。

事件驱动是怎样用在浏览器中的 JavaScript 的?

借助引擎,JavaScript 可以运行在你的浏览器中

最受欢迎的 JavaScript 引擎是 Google Chrome 和 Node.js 所使用的V8,Firefox 的 SpiderMonkey 和 Safari/WebKit 使用的 JavaScriptCore。

基于供丰富的环境,JavaScript 引擎增强了语言,还提供了事件驱动的 JavaScript 平台

实际上,浏览器中的 JavaScript 可以与 HTML 元素进行交互,这些 HTML 元素是事件发送器(event emitters),即能够发送事件的对象

思考一下这个简单的例子,一个带有按钮的 HTML 文档:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>What means "event-driven" in JavaScript?</title>
</head>
<body>
<div>
    <button id="subscribe">SUBSCRIBE</button>
</div>
</body>
</html>

如果没有 JavaScript,则这个按钮将毫无生命。现在 HTML 按钮是 HTMLButtonElement 【https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLButtonElement】类型的元素,并且与所有 HTML 元素一样,它们都连接到 EventTarget —— 每个 HTML 元素的共同祖先。

浏览器中的事件目标是能够发出事件的对象:它们是观察者模式中的主题

有点混乱?请记住:主题是 FM 广播,所以任何 HTML 元素都像是广电台。

一会儿,你将看到谁是观察者

浏览器中的主题和观察者

如果 HTML 元素是主题,那么谁是观察者?任何注册为侦听器的 JavaScript 函数都可以对浏览器中的事件做出反应。

使用 JavaScript 选择一个 HTML 元素:

const btn = document.getElementById('subscribe');

并使用 addEventListener 注册侦听器

const btn = document.getElementById('subscribe');
btn.addEventListener("click", function () {
    console.log("Button clicked");
});

这里的“click”是事件,按钮是主题,或者是发送器,函数是侦听器,或者是观察者

回顾一下:

HTML 元素事件发送器

JavaScript 中注册为侦听器的函数是观察者

所有这些组件构成了“一个小小的事件驱动的体系结构。要测试代码请保存下面的 HTML 内容到文件(或在 Codepen 上尝试),请单击按钮,然后查看浏览器的控制台:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>What means "event-driven" in JavaScript?</title>
</head>
<body>
<div>
    <button id="subscribe">SUBSCRIBE</button>
</div>
</body>
<script>
    const btn = document.getElementById('subscribe');
    btn.addEventListener("click", function () {
        console.log("Button clicked");
    });
</script>
</html>

在下一部分中,你将看到用于 Node.js 的相同概念。

事件驱动如何用于 Node.js?

Node.js 是用于基于 V8 引擎的运行在浏览器之外(命令行工具和服务器端)的 JavaScript 环境。

你在 Node.js 中所做的大部分工作都是基于事件的。总会有一个发送器对象,一些观察者在监听消息。

在 Node.js 中,没有任何 HTML 元素,因此大多数事件都来自进程、与网络的交互、文件等。

Node.js 中的每个事件发送器都有一个名为 on 的方法,该方法至少需要两个参数:

  • 要侦听的事件的名称
  • 监听器函数

让我们举一个实际的例子。看一下这个简单的 Node.js 服务器:

const net = require("net");
const server = net.createServer().listen(8081, "127.0.0.1");
server.on("listening", function () {
  console.log("Server listening!");
});
server.on("connection", function (socket) {
  console.log("Client connected!");
  socket.end("Hello client!");
});

这段代码创建了一个监听本地主机端口 8081 的服务器。在 server 对象上,我们调用 on 方法来注册两个侦听器函数。

服务器启动后立即触发 listening 事件,而客户端连接到 127.0.0.1:8081 时将触发 connection 事件(尝试一下!)。

在此示例中,server 是事件发送器,主题。另一方面,侦听器函数是观察者

但是那些 on 方法从哪里来的呢?

了解 EventEmitter

Node.js 中的所有事件驱动模块都扩展了一个名为 EventEmitter 的根类。在我们之前的例子中,来自 net 模块的网络服务器就使用了 EventEmitter。

Node.js 中的 EventEmitter 有两种基本方法:onemit

如果你想要与浏览器对应,那么可以把 EventEmitter 看作是能够发出事件的任何一种 HTML 元素。

要在浏览器中侦听事件,请在主题对象上调用 addEventListener

const btn = document.getElementById('subscribe');
btn.addEventListener("click", function () {
    console.log("Button clicked");
});

相反,在 Node.js 中有 on

// omit
server.on("listening", () => {
  console.log("Server listening!");
});
// omit

准确地说,EventEmitter 上还有一个 addListener 方法。on 是它的别名。

EventEmitter 还有一个 emit 方法,在你广播自定义事件(消息)时很有用。

如果要使用 EventEmitter ,请从 “events” 模块中导入并发出事件:

const EventEmitter = require("events");
const emitter = new EventEmitter();
emitter.on("customEvent", () => console.log("Got event!"));
emitter.emit("customEvent");

用 Node.js 运行代码,你将在控制台中看到 “Got event”。

JavaScript 中有关观察者/发布-订阅的其他示例

JavaScript 没有对观察者对象的原生支持,但是有人建议将其添加到语言中。

RxJS 是一个将观察者模式引入 JavaScript 的库。【http://reactivex.io/】

Redux 是 JavaScript 中发布-订阅模式的实现。这是一个非常好的事件发送器,其中状态的更改会被分发给所有监听的观察者。【https://www.valentinog.com/blog/redux/】

现代浏览器附带 Intersection Observer API,这是观察者模式的另一个例子。【https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API】

Socket.IO 是一个库,大量使用了事件。【https://www.valentinog.com/blog/socket-react/#The_WebSocket_protocol_Nodejs_and_SocketIO】

总结

希望你从这篇文章中学到新的东西。你学到了很多术语,但最终都归结为大约 30 年前发明的模式:发布-订阅

这种模式,也称为观察者,是我们今天在 JavaScript 和 Node.js 中所使用的事件驱动架构的基础。

再次强调,事件驱动、发布-订阅和观察者的模式并非完全相同:事件驱动的体系结构建立在发布-订阅之上,观察者模式比 DOM 和 Node.js 事件更丰富。

但他们都是属于同一个家庭的成员。

原文:https://www.valentinog.com/blog/event/

本文分享自微信公众号 - 前端先锋(jingchengyideng),作者:疯狂的技术宅

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-12-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Node.js 究竟是什么?

    Node.js 是一个 JavaScript 运行时环境。听起来还不错,不过这究竟意味着什么?它又是如何运作的?

    疯狂的技术宅
  • Node 事件循环究竟是如何工作的: 为何大部分的事件循环图都是错的

    当 Bert 在 2016 年欧洲 Node 交流大会上提出关于事件循环的主题时,他以一句“大部分的事件循环图都是错的”开场。我很愧疚,我演讲中也用过一些错误的...

    疯狂的技术宅
  • 为什么要用 Node.js?[每日前端夜话0x77]

    JavaScript 的日益发展带来了很多变化,当今的 Web 开发面貌已经变得截然不同。在几年前是很难想象在服务器上运行 JavaScript 的。

    疯狂的技术宅
  • Node.js 是什么?我为什么选择它?

    当我们学习一项新的事物的时候,我们首先要知道它来自哪里?它是什么?能做什么或者换句话说,能解决什么问题?没有一样东西是最好的,是可以替代所有的,但在某一领域它是...

    五月君
  • Node.js 究竟是什么?

    Node.js 是一个 JavaScript 运行时环境。听起来还不错,不过这究竟意味着什么?它又是如何运作的?

    疯狂的技术宅
  • 重要文章分类目录

    《深入浅出Node.js》:Node异步编程解决方案 之 生成器(Generator)函数

    前端_AWhile
  • 如何在2016年成为一个更好的Node.js开发者

    本文主要讨论一些进行Node.js开发的最佳实践和建议,这些建议不仅仅适合开发者,还适合那些管理与维护Node.js基础架构的工作人员。遵循本文提供的这些建议,...

    春哥大魔王
  • 异步与回调/函数的作用域链

    程序里面所有的任务,可以分成两类:同步任务(synchronous)和异步任务(asynchronous)。

    代码之风
  • org.apache.ibatis.binding.BindingException: Mapper method '...' attempted to return null from a m...

    一个会写诗的程序员
  • 重塑经典 | QQ复古头像再设计

    ? 腾讯ISUX isux.tencent.com 社交用户体验设计 ? ? 重新构想复古怀旧,赋予我们喜爱的人物角色更多个性、温度、色彩和熟识度,以提升用...

    腾讯ISUX

扫码关注云+社区

领取腾讯云代金券