改善程序性能和代码质量:通过代理模式组合HTTP请求

网络 通信技术
在前端项目中,我们的网页通常需要向服务器发送多个HTTP请求。假设我们的产品具有一项功能,即每当用户单击 li 标记时,客户端都会向服务器发送一个HTTP请求。

[[387350]]

原文:https://levelup.gitconnected.com/improve-both-app-performance-and-code-quality-combining-http-requests-by-proxy-pattern-2cce132d60

作者:bitfish

在前端项目中,我们的网页通常需要向服务器发送多个HTTP请求。

假设我们的产品具有一项功能,即每当用户单击 li 标记时,客户端都会向服务器发送一个HTTP请求。

这是一个简单的Demo:

  1. <html> 
  2.  
  3. <body> 
  4.     <ul> 
  5.         <li>1</li> 
  6.         <li>2</li> 
  7.         <li>3</li> 
  8.         <li>4</li> 
  9.         <li>5</li> 
  10.         <li>6</li> 
  11.         <li>7</li> 
  12.         <li>8</li> 
  13.         <li>9</li> 
  14.     </ul> 
  15.  
  16.     <script> 
  17.         // Suppose this function is used to make HTTP requests to the server 
  18.         var sendHTTPRequest = function(message) { 
  19.             console.log('Start sending HTTP message to the server: ', message) 
  20.             console.log('1000ms passed'
  21.             console.log('HTTP Request is completed'
  22.         } 
  23.  
  24.         var ul = document.getElementsByTagName('ul')[0]; 
  25.  
  26.         ul.onclick = function(event) { 
  27.             if (event.target.nodeName === "LI") { 
  28.  
  29.                 // Executes this function every time the <li> tag is clicked. 
  30.                 sendHTTPRequest(event.target.innerText) 
  31.             } 
  32.         } 
  33.     </script> 
  34. </body> 
  35.  
  36. </html> 

 

 

 

 

在上面的代码中,我们直接使用简单的 sendHTTPRequest 函数来模拟发送HTTP请求。这样做是为了更好地专注于核心目标,因此我简化了一些代码。

在上面的代码中,我们直接使用简单的 sendHTTPRequest 函数来模拟发送HTTP请求。这样做是为了更好地专注于核心目标,因此我简化了一些代码。

 

 

上面的程序是这样的:

为了使你们更容易尝试,我制作了一个Codepen演示:https://codepen.io/bitfishxyz/pen/PobOZMm

当然,在真实的项目中,我们可能会向服务器发送一个文件,推送通知,或者发送一些日志。但为了演示的惯例,我们将跳过这些细节。

好了,这是一个很简单的演示,那么上面的代码有没有什么缺点呢?

如果您的项目非常简单,那么编写这样的代码应该没有问题。但是,如果您的项目很复杂,并且客户端需要频繁向服务器发送HTTP请求,则此代码效率很低。

在上面的示例中,如果任何用户反复快速单击 li 元素会发生什么?这时,我们的客户端需要向服务器发出频繁的HTTP请求,并且每个请求都会消耗大量时间和服务器资源。

客户端每次与服务器建立新的HTTP连接时,都会消耗一些时间和服务器资源。因此,在HTTP传输机制中,一次传输所有文件比多次传输少量文件更为有效。

例如,您可能需要发送五个HTTP请求,每个HTTP请求的HTTP数据包大小为1MB。现在,您一次发送一个HTTP请求,数据包大小为5MB。通常预期后者的性能要比前一个更好。

网页上的大量HTTP请求可能会减慢网页的加载时间,最终损害用户体验。如果加载速度不够快,这可能会导致访问者更快地离开该页面。

因此,在这种情况下,我们可以考虑合并HTTP请求。

在我们目前的项目中,我的思路是这样的:我们可以在本地设置一个缓存,然后在一定范围内收集所有需要发送给服务器的消息,然后一起发送。

你可以暂停一下,自己试着想办法。

提示:您需要创建一个本地缓存对象来收集需要发送的消息。然后,您需要使用定时器定时发送收集到的消息。

这是一个实现。

  1. var messages = []; 
  2. var timer; 
  3. var sendHTTPRequest = function (message) { 
  4.   messages.push(message); 
  5.   if (timer) { 
  6.     return
  7.   } 
  8.   timer = setTimeout(function () { 
  9.     console.log("Start sending messages: ", messages.join(",")); 
  10.     console.log("1000ms passed"); 
  11.     console.log("HTTP Request is completed."); 
  12.  
  13.     clearTimeout(timer); 
  14.     timer = null
  15.     messages = []; 
  16.   }, 2000); 
  17. }; 

每当客户端需要发送消息,也就是触发一个 onclick 事件的时候,sendHTTPRequest 并不会立即向服务器发送消息,而是先将消息缓存在消息中。然后,我们有一个计时器,该计时器在2秒钟后执行,并且在2秒钟后,该计时器会将所有先前缓存的消息发送到服务器。此更改达到了组合HTTP请求的目的。

测试结果如下:

如你所见,尽管我们多次触发点击事件,但在两秒钟内,我们只发送了一个HTTP请求。

当然,为了方便演示,我将等待时间设置为2秒。如果你觉得这个等待时间太长,你可以缩短这个等待时间。

对于不需要太多实时交互的项目,2秒的延迟并不是一个巨大的副作用,但它可以减轻服务器的很多压力。在适当的情况下,这是非常值得的。

上面的代码确实为项目提供了一些性能改进。但是就代码设计而言,上面的代码并不好。

第一,违反了单一责任原则。sendHTTPRequest 函数不仅向服务器发送HTTP请求,而且还组合HTTP请求。该函数执行过多操作,使代码看起来非常复杂。

如果某个功能(或对象)承担了过多的责任,那么当我们的需求发生变化时,该功能通常将不得不发生重大变化。这样的设计不能有效地应对可能的更改,这是一个糟糕的设计。

我们理想的代码如下所示:

我们没有对 sendHTTPRequest 进行任何更改,而是选择为其提供代理。这个代理函数执行合并HTTP请求的任务,并将合并后的消息传递给 sendHTTPRequest 发送。然后我们以后就可以直接使用 proxySendHTTPRequest 方法了。

您可以暂停片刻,然后尝试自己解决。

这是一个实现:

  1. var proxySendHTTPRequest = (function() { 
  2.   var messages = [], 
  3.       timer; 
  4.   return function(message) { 
  5.     messages.push(message); 
  6.     if (timer) { 
  7.       return
  8.     } 
  9.     timer = setTimeout(function() { 
  10.       sendHTTPRequest(messages.join(",")); 
  11.       clearTimeout(timer); 
  12.       timer = null
  13.       messages = []; 
  14.     }, 2000); 
  15.   }; 
  16. })(); 

其基本思想与前面的代码类似,该代码使用 messages 变量在一定时间内缓存所有消息,然后通过计时器统一地发送它们。此外,这段代码使用了闭包技巧,将 messages 和 timer 变量放在局部作用域中,以避免污染全局名称空间。

这段代码与前面的代码最大的区别是它没有更改 sendHTTPRequest 函数,而是将其隐藏在 proxySendHTTPRequest 后面。我们不再需要直接访问 sendHTTPRequest,而是使用代理 proxySendHTTPRequest 来访问它。proxySendHTTPRequest 与sendHTTPRequest 具有相同的参数列表和相同的返回值。

这样的设计有什么好处?

  • 发送HTTP请求和合并HTTP请求的任务交给了两个不同的函数,每个函数专注于一个职责。它遵从单一责任原则,并使代码更容易理解。
  • 由于两个函数的参数是相同的,我们可以简单地用 proxySendHTTPRequest 替换 sendHTTPRequest 的位置,而不需要做任何重大更改。

想象一下,如果将来网络性能有所提高,或者由于某些其他原因,我们不再需要合并HTTP请求。在这一点上,如果我们使用以前的设计,我们将不得不再次大规模地更改代码。在当前的代码设计中,我们可以简单地替换函数名。

事实上,这个编码技巧通常被称为设计模式中的代理模式。

所谓的代理模式,其实在现实生活中很好理解。

  • 比方说,你想访问一个网站,但你不想泄露你的IP地址。那么你可以使用,先访问你的代理服务器,然后通过代理服务器访问目标网站。这样目标网站就无法知道你的IP地址了。
  • 有时候,你会把你的真实服务器隐藏在Nginx服务器后面,让Nginx服务器为你的真实服务器处理一些琐碎的操作。

这些都是现实生活中代理模式的例子。

我们不需要为代理模式(或任何其他设计模式)的正式定义而烦恼,我们只需要知道,当客户端没有直接访问它的便利(或能力)时,我们可以提供代理功能(或对象)来控制对目标功能(或对象)的访问即可。客户机实际上访问代理函数(或对象),代理函数对请求进行一些处理,然后将请求传递给目标。

本文转载自微信公众号「前端全栈开发者」,可以通过以下二维码关注。转载本文请联系前端全栈开发者公众号。

 

责任编辑:武晓燕 来源: 前端全栈开发者
相关推荐

2012-11-28 11:09:28

IBMdW

2020-12-29 15:00:46

PerfVTune工具

2010-08-10 13:58:00

Flex性能测试

2020-12-08 06:24:08

Java 代码工具

2010-02-04 09:41:03

Android应用程序

2019-03-01 09:55:28

HTTPMock架构

2010-07-28 09:25:41

jQueryJavaScript性

2021-08-18 09:37:51

数据库移动应用程序

2010-06-11 10:19:22

systemd

2018-11-20 10:50:00

Java性能优化编程技巧

2013-12-17 17:05:20

iOS性能优化

2009-07-01 18:24:59

JSP应用程序JMeter

2019-02-01 09:50:00

提升Python程序性能

2019-10-17 10:10:23

优化Web前端

2024-11-04 11:02:56

2018-11-06 09:53:27

2012-05-19 22:24:34

MVVM

2010-05-20 18:40:33

IIS服务器

2023-12-13 13:41:00

代码Java程序员

2009-06-15 09:47:12

Java程序内存溢出
点赞
收藏

51CTO技术栈公众号