Node.js 实战 4.Socket.IO 去掉传入参数影响

在现代的 Web 应用程序中,实时性已经逐渐成为一个基本要素。而其中最简单高效的解决方案之一,就是利用 Socket.IO

不过在实际开发过程中,很多人会遇到一个麻烦的问题,即:Socket.IO 的 emiton 方法,第一个参数通常是事件名称,而第二个参数则是待发送的数据。然而这种方法显然很不灵活,因为你无法直接控制是否发送第二个参数。而有时候,我们需要在事件名称的基础上再添加一些其他辅助信息,才能满足业务需求。

这篇文章就要告诉你,如何利用 JavaScript 的“偏函数”概念,解决这个问题,让你的 Socket.IO 应用变得更加灵活和健壮!

偏函数概述

所谓“偏函数”,顾名思义就是“部分应用一个函数”。也就是说,给定一个函数和部分参数,可以产生另外一个函数。

我们来看个例子。假设你有一个加法函数:

现在你需要一个函数,它总是加上 5。你可以这样做:

可以看出,addFive 变成了 add 的“偏函数”。

如果换一个场景,假设你需要做一个高阶函数,把 add6 封装到一个函数里:

这时候你就可以直接调用 otherFunc(add, 4),结果是 10。

了解了偏函数的概念,我们再来看看如何运用到 Socket.IO 上。

针对 Socket.IO 的问题

首先我们看一下 Socket.IO 常用的 emiton 方法,以及它们暴露的接口:

这里的 'message' 参数即为事件名称,data 则为需要发送的数据。然而,这样的设计并不太好,因为你无法将事件名称与其他参数“拼合”起来,只能在名称前面加一些固定的字符,比如 client-messageserver-message

如果你需要一些更复杂的业务逻辑,例如在前端 Angular 应用中使用 WebSocket,实现一个 “通过用户名发送信息” 的功能。会出现这样一种调用:

很遗憾,这么做显然是有问题的。考虑到 Socket.IO 允许一个事件可以有多个监听器,另一个使用 socket.on 监听该事件的地方也可能是这样调用的:

两边之间的信息不匹配,很有可能会造成难以预料的错误。

为什么会出现这个问题?是因为 Socket.IO 的固有设计限制吗?其实不是的。如果我们能够将“事件名称”与“其他参数”分离,把它作为一个对象传进去,就很容易解决这个问题了。

事实上,JavaScript 已经提供了相应的方法,bind。我们可以这样写:

在服务端的实现也类似:

但是这样写并不优雅,需要大量重复代码,而且不容易维护。这边提供了一种更好的解决方法,即利用 JavaScript 的“偏函数”概念。

解决方法:深度应用偏函数

有了偏函数,我们可以有自动为 emiton 添加事件名称功能的函数,代码如下:

如上所示,我们通过 curryEmitcurryOn 获得了一个“部分应用”的函数。即,在调用 curryEmit(socket, 'user-message') 时,会返回一个新的函数,这个新函数在调用时再传入 msg 参数。当我们需要发送 user-message 类型事件时,就可以直接调用此函数,像这样:

这在后续的代码编写中,非常方便。

同样,我们也可以使用 curryOn 实现将特定类型的事件附加到 socket.on 上的需求:

这样的好处是在之后的代码维护过程中,改变事件名称只需要改动一处,即 curryEmitcurryOn 调用的地方即可,其他代码不受影响。

当然,我们也可以使用一些高级技巧,将允许多个事件名称以及多个参数的情况下,使用“偏函数”的方式实现。如果你有了解拓展函数,可以参考如下代码实现:

实际案例—Angular + Socket.IO

以上理论处理了“事件名称”和“消息体”的问题之后,我们再来看一个实际的案例,也就是 Angular 客户端通过 Socket.IO 与服务器交互,发送和接受消息。

服务端代码非常基础,就不展开介绍了,读者可以直接到 GitHub 地址 查看。

下面是前端代码实现,首先我们需要引入 Socket.IO:

接着连接 Socket.IO 服务器:

在需要发送消息给服务端的地方调用:

代码中,curryEmit 函数接受一个 Socket.IO 的 socket 对象和一个事件名称,返回一个新的函数以便之后随时发送此事件。使用 curryEmit 函数我们生成了 send 函数,随时调用:

这时候就可以发送带参数的事件名称 user-message:{username} 给后端。

完整代码实现可以在 GitHub 上找到。

总结

在本文中,我们讲述了 Socket.IO 中常见的问题,以及这个问题带来的不便。随后我们介绍了偏函数的概念,并通过一些简单的示例说明了它的基本用法。最后,我们通过一个实际的案例,向读者展示了怎么样基于 Socket.IO 以及 Angular 实现一个更加灵活、健壮的“实时消息传递”应用。

“偏函数”之于 JavaScript,就像汽车之于路面,一样重要。它不仅提供了一种高效的设计思路,还能让我们的代码变得更加灵活、可读性和可维护性大大增强。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/653dac487d4982a6eb768573


纠错
反馈