改善應用程式效能和程式碼質量:透過代理模式組合HTTP請求

翻譯自levelup。gitconnected。com

作者:bitfish

在前端專案中,我們的網頁通常需要向伺服器傳送多個HTTP請求。

假設我們的產品具有一項功能,即每當使用者單擊 li 標記時,客戶端都會向伺服器傳送一個HTTP請求。

這是一個簡單的Demo:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在上面的程式碼中,我們直接使用簡單的 sendHTTPRequest 函式來

模擬

傳送HTTP請求。這樣做是為了更好地專注於核心目標,因此我簡化了一些程式碼。

然後,我們將click事件繫結到 ul 元素。每次使用者單擊諸如

  • 5
  • 之類的標記時,客戶端將執行 sendHTTPRequest 函式以向伺服器發出HTTP請求。

    上面的程式是這樣的:

    改善應用程式效能和程式碼質量:透過代理模式組合HTTP請求

    為了使你們更容易嘗試,我製作了一個Codepen演示:codepen。io/bitfishxyz/…

    當然,在真實的專案中,我們可能會向伺服器傳送一個檔案,推送通知,或者傳送一些日誌。但為了演示的慣例,我們將跳過這些細節。

    好了,這是一個很簡單的演示,那麼上面的程式碼有沒有什麼缺點呢?

    如果您的專案非常簡單,那麼編寫這樣的程式碼應該沒有問題。但是,如果您的專案很複雜,並且客戶端需要頻繁向伺服器傳送HTTP請求,則此程式碼效率很低。

    在上面的示例中,如果任何使用者反覆快速單擊 li 元素會發生什麼?這時,我們的客戶端需要向伺服器發出頻繁的HTTP請求,並且每個請求都會消耗大量時間和伺服器資源。

    客戶端每次與伺服器建立新的HTTP連線時,都會消耗一些時間和伺服器資源。因此,在HTTP傳輸機制中,一次傳輸所有檔案比多次傳輸少量檔案更為有效。

    例如,您可能需要傳送五個HTTP請求,每個HTTP請求的HTTP資料包大小為1MB。現在,您一次傳送一個HTTP請求,資料包大小為5MB。通常預期後者的效能要比前一個更好。

    網頁上的大量HTTP請求可能會減慢網頁的載入時間,最終損害使用者體驗。如果載入速度不夠快,這可能會導致訪問者更快地離開該頁面。

    因此,在這種情況下,我們可以考慮合併HTTP請求。

    在我們目前的專案中,我的思路是這樣的:我們可以在本地設定一個快取,然後在一定範圍內收集所有需要傳送給伺服器的訊息,然後一起傳送。

    你可以暫停一下,自己試著想辦法。

    提示:您需要建立一個本地快取物件來收集需要傳送的訊息。然後,您需要使用定時器定時傳送收集到的訊息。

    這是一個實現。

    改善應用程式效能和程式碼質量:透過代理模式組合HTTP請求

    var messages = [];var timer;var sendHTTPRequest = function (message) { messages。push(message); if (timer) { return; } timer = setTimeout(function () { console。log(“Start sending messages: ”, messages。join(“,”)); console。log(“1000ms passed”); console。log(“HTTP Request is completed。”); clearTimeout(timer); timer = null; messages = []; }, 2000);};

    每當客戶端需要傳送訊息,也就是觸發一個 onclick 事件的時候,sendHTTPRequest 並不會立即向伺服器傳送訊息,而是先將訊息快取在訊息中。然後,我們有一個計時器,該計時器在2秒鐘後執行,並且在2秒鐘後,該計時器會將所有先前快取的訊息傳送到伺服器。此更改達到了組合HTTP請求的目的。

    測試結果如下:

    改善應用程式效能和程式碼質量:透過代理模式組合HTTP請求

    如你所見,儘管我們多次觸發點選事件,但在兩秒鐘內,我們只發送了一個HTTP請求。

    當然,為了方便演示,我將等待時間設定為2秒。如果你覺得這個等待時間太長,你可以縮短這個等待時間。

    對於不需要太多實時互動的專案,2秒的延遲並不是一個巨大的副作用,但它可以減輕伺服器的很多壓力。在適當的情況下,這是非常值得的。

    上面的程式碼確實為專案提供了一些效能改進。但是就程式碼設計而言,上面的程式碼並不好。

    第一,違反了單一責任原則。sendHTTPRequest 函式不僅向伺服器傳送HTTP請求,而且還組合HTTP請求。該函式執行過多操作,使程式碼看起來非常複雜。

    如果某個功能(或物件)承擔了過多的責任,那麼當我們的需求發生變化時,該功能通常將不得不發生重大變化。這樣的設計不能有效地應對可能的更改,這是一個糟糕的設計。

    我們理想的程式碼如下所示:

    改善應用程式效能和程式碼質量:透過代理模式組合HTTP請求

    我們沒有對 sendHTTPRequest 進行任何更改,而是選擇為其提供代理。這個代理函式執行合併HTTP請求的任務,並將合併後的訊息傳遞給 sendHTTPRequest 傳送。然後我們以後就可以直接使用 proxySendHTTPRequest 方法了。

    您可以暫停片刻,然後嘗試自己解決。

    這是一個實現:

    改善應用程式效能和程式碼質量:透過代理模式組合HTTP請求

    var proxySendHTTPRequest = (function() { var messages = [], timer; return function(message) { messages。push(message); if (timer) { return; } timer = setTimeout(function() { sendHTTPRequest(messages。join(“,”)); clearTimeout(timer); timer = null; messages = []; }, 2000); };})();

    其基本思想與前面的程式碼類似,該程式碼使用 messages 變數在一定時間內快取所有訊息,然後透過計時器統一地傳送它們。此外,這段程式碼使用了閉包技巧,將 messages 和 timer 變數放在區域性作用域中,以避免汙染全域性名稱空間。

    這段程式碼與前面的程式碼最大的區別是它沒有更改 sendHTTPRequest 函式,而是將其隱藏在 proxySendHTTPRequest 後面。我們不再需要直接訪問 sendHTTPRequest,而是使用代理 proxySendHTTPRequest 來訪問它。proxySendHTTPRequest 與sendHTTPRequest 具有相同的引數列表和相同的返回值。

    這樣的設計有什麼好處?

    傳送HTTP請求和合並HTTP請求的任務交給了兩個不同的函式,每個函式專注於一個職責。它遵從單一責任原則,並使程式碼更容易理解。

    由於兩個函式的引數是相同的,我們可以簡單地用 proxySendHTTPRequest 替換 sendHTTPRequest 的位置,而不需要做任何重大更改。

    想象一下,如果將來網路效能有所提高,或者由於某些其他原因,我們不再需要合併HTTP請求。在這一點上,如果我們使用以前的設計,我們將不得不再次大規模地更改程式碼。在當前的程式碼設計中,我們可以簡單地替換函式名。

    事實上,這個編碼技巧通常被稱為設計模式中的

    代理模式

    所謂的代理模式,其實在現實生活中很好理解。

    比方說,你想訪問一個網站,但你不想洩露你的IP地址。那麼你可以使用VPN,先訪問你的代理伺服器,然後透過代理伺服器訪問目標網站。這樣目標網站就無法知道你的IP地址了。

    有時候,你會把你的真實伺服器隱藏在Nginx伺服器後面,讓Nginx伺服器為你的真實伺服器處理一些瑣碎的操作。

    這些都是現實生活中代理模式的例子。

    我們不需要為代理模式(或任何其他設計模式)的正式定義而煩惱,我們只需要知道,當客戶端沒有直接訪問它的便利(或能力)時,我們可以提供代理功能(或物件)來控制對目標功能(或物件)的訪問即可。客戶機實際上訪問代理函式(或物件),代理函式對請求進行一些處理,然後將請求傳遞給目標。

    作者:杭州程式設計師張張

    連結:https://juejin。cn/post/6939782564836016158