首頁 >web前端 >js教程 >Chunk-Busters:不要跨越溪流!

Chunk-Busters:不要跨越溪流!

Linda Hamilton
Linda Hamilton原創
2024-12-02 05:00:10840瀏覽

⚠️ 如果您有光敏性,您可能想跳過此操作。
請參閱下面的靜態圖片,這些燈將開始快速閃爍!

Chunk-Busters: Don’t cross the Streams!

互聯網如何運作?

記住標題…我們在這裡討論的是流。

我可以談論協定、資料包、排序、acks 和 nacks…但我們在這裡談論流,正如你可能猜對了(我相信你=D)流…它要么是二進制,要么是字串。

是的,字串在發送之前會被壓縮…但是對於我們在前後端開發中通常關心的內容…字串和二進位。

在下面的範例中,我將使用 JS 流。

雖然 Node 有自己的遺留實現,但我們有辦法處理相同程式碼的流,無論是在前面還是後面。

其他語言有自己處理流的方式,但正如你所看到的......處理它的實際程式碼部分並不復雜(並不是說沒有發生複雜的事情)。

範例問題

您的前端必須使用多個來源的資料。

雖然您可以透過其 IP/連接埠單獨存取每個來源,但您可以將它們放在 API 閘道後面以便於使用和控制。

回購協議

檢查連結中的儲存庫,了解如何自己運行它,以便您可以使用它。

https://github.com/Noriller/chunk-busters

影片

後續影片版本:

https://youtu.be/QucaOfFI0fM

v0 - 簡單的實現

您擁有來源,您可以取得、等待和渲染。沖洗並重複。

await fetch1();
handleResult(1);
await fetch2();
handleResult(2);
...
await fetch9();
handleResult(9);

你可能會認為沒有人會真正這麼做......

在這個例子中,很明顯出了問題,但陷入這個問題並不難。

顯而易見:它很慢。你必須觸發並等待每個請求,如果速度很慢…你必須等待。

v1 - 渴望版本

您知道您不想單獨等待每個請求......因此您觸發所有請求,然後等待它們完成。

await Promise.all([
  fetch1(),
  fetch2(),
  ...
  fetch9(),
]);
handleAllResults(results);

這就是你可能會做的事情,所以這很好,對吧?

我的意思是,除非您有一個請求速度很慢……這意味著即使所有其他請求都已完成……您仍然必須等待該請求完成。

v2 - 更聰明、更熱心的版本

你知道你可能有一些較慢的請求,所以你仍然觸發所有請求並等待,但當它們到來時,你已經在可能的情況下對結果做了一些事情,所以當最後一個請求到達時,其他請求已經完成。

await fetch1();
handleResult(1);
await fetch2();
handleResult(2);
...
await fetch9();
handleResult(9);

這一定是最好的解決方案,對吧?

嗯……有什麼奇怪的嗎?

v3 - 我在騙你…這就是 v1 該的樣子

還記得 v1 嗎?是的...它應該是這樣的:

事實證明,在 http/1 中同一個端點可以擁有的連線數量是有限制的,不僅如此…它還依賴瀏覽器,並且每個瀏覽器可能有不同的限制。

您可能會認為只使用 http/2 就到此為止了…但即使這是一個很好的解決方案,您仍然需要在前端處理多個端點。

有沒有好的解決方案?

v4 - 進入流!

讓我們回顧一下 v0,但使用流......

你很聰明,所以你可能已經預料到了這一點,因為警告有點破壞了它......但是,是的......你之前看到的並不是後端生成的所有數據。

無論如何…當我們取得時我們就會渲染。

await Promise.all([
  fetch1(),
  fetch2(),
  ...
  fetch9(),
]);
handleAllResults(results);

如果我們點擊即將到來的串流,我們就可以對它到來的資料塊做一些事情。 (是的!就像 Chat GPT 之類的一樣。)

即使 v0 是處理這個問題最糟糕的方法,但透過使用流可以大大改善它。即使總等待時間相同,您也可以透過顯示某些內容來欺騙使用者。

再次是 v5 - v1,但是帶有流!

http/1 問題仍然是一個問題,但同樣,您已經可以看到事情的來龍去脈。

是的…我不能再拖延了…所以…

v6 - 一個 API 來統治它們!

或…也許我可以?

你看,前端必須管理太多......如果我們可以將其卸載到後端,那麼您就可以擁有一個端點來處理所有來源。

這解決了前端的複雜性和 http/1 問題。

await Promise.all([
  fetch1().then(handleResult),
  fetch2().then(handleResult),
  ...
  fetch9().then(handleResult),
]);


// usually we do this:
await fetch(...).then((res) => {
  // this json call accumulate all the response
  // that later is returned for you to use
  return res.json()
})

v7 - 最後......一個 API、多個來源和串流媒體。

我們呼叫一個 API,它將呼叫所有來源、串流資料、處理數據,並將其傳遞到前端,而前端又會在資料到來時呈現資料。

用於此的程式碼正面和背面基本上相同:

await fetchAll();
handleAllResults(results);

是的……就是這樣(最基本、最簡單的例子)。

我們將字串加入到緩衝區中,解析它,檢查是否有可用的區塊,使用它,然後忘記它。這意味著您可以接收/消耗 TB 級的資料…一次一大塊,而 RAM 很少。

我知道你在想什麼...這很愚蠢......這也是瘋狂......

MOOOOOOOOM 我想要 Websocket!

不,親愛的,我們家裡有網路套接字!

家裡的 Websocket:下一個?

v8 - 如果它不起作用,那才是愚蠢的

你很聰明,你認為如果來源仍在產生資料......那麼也許我們可以更新一些變數......

透過這種方式,您可以保持一個連線用於獲取更多資料或更改其產生的內容。

是的......我想你可以做到這一點......並且在你的堅持下我做了這個例子。 =D

仍然......這是一個愚蠢的想法,我不知道它在哪裡/是否可以在真實的生產環境中使用。也許如果你回到 MPA 和 Ajax 之間那個尷尬的 JS 階段,在這個階段你有足夠的互動性,但沒有足夠的連接到同一伺服器(某些瀏覽器的限制只有 2 個!),那麼也許?

除此之外,不知道。如果您確實有……請告訴我。

在上面的範例中,注意中間的面板,尤其是「進度邊框」:你可以看到它不斷更新。如果您開啟網路選項卡,您會看到 GET 連線在結束之前從未關閉。您還會看到多個其他請求,這些請求改變了那個仍然存在的連接正在執行的操作…所有這些都使用普通的 http/1。

接下來是什麼?

字串與 JSON

這個例子是我能做的最基本的例子。我什至使用簡單的字串而不是 JSON,因為它更容易解析。

要使用 JSON,您必須累積字串(出於某種原因,我們必須對後端回應進行 JSON.stringify)。

然後檢查在哪裡打破它,然後解析該值或邊解析邊解析。

對於第一個,請考慮 NDJSON:而不是 JSON 數組,您可以用換行符號分隔對象,然後您可以“更輕鬆地”找到中斷的位置,然後 JSON.parse 每個對象並使用該對象。

對於後者,你可以邊解析邊解析:你知道你在一個數組中,現在它是一個對象,好的第一個鍵,現在它是鍵的值,下一個鍵,跳過那個,下一個鍵......等等……手動製作並不是一件簡單的事情,但這就像在等待時從等待然後渲染到渲染的跳轉,這一切都是關於……除了……規模更小。

錯誤處理

人們喜歡託管範例,這個您需要自己運行...我希望現在不將範例託管在某個地方的原因已經清楚,但另一個原因是我們不希望這裡出現任何錯誤,如果您要這樣做的話加入網路錯誤高於一切......好吧......

應該要處理錯誤,但它們確實增加了另一層複雜性。

你應該使用它嗎?

也許...你可以說取決於...

有些地方串流是答案,但在大多數情況下......await json 就足夠了(更不用說更容易了)。

但是學習流程可以解決一些問題,無論是在前端或後端。

在前端,你總是可以用它來「欺騙」使用者。您可以在某些內容出現時顯示它,然後在出現時顯示更多內容,而不是到處顯示旋轉器,即使這需要一段時間。只要您不阻止用戶與其互動...您甚至可以製作比僅顯示旋轉器「更慢」的東西感覺就像它比任何東西都快實際上更快.

在後端,您可以節省 RAM,因為您可以解析每個資料塊,無論是來自前端、資料庫還是中間的任何其他資料。根據需要處理數據並發送數據,而不必等待整個有效負載,否則會引發 OOM(記憶體不足)錯誤。 GB 甚至 TB 的數據…當然,為什麼不呢?

尾奏

React 慢嗎?整個範例前端是用 React 完成的,除了所有閃爍的「燈」發生的「主要」事情之外,還有很多其他事情發生。

是的......如果你走得夠快,範例就無法跟上並開始凍結。但由於每分鐘很容易進行數千個渲染……我確實認為這對於大多數應用程式來說已經足夠了。

並且,您始終可以提高性能:對於“進度邊框”,如果您需要在渲染中保存一些內容,我使用了延遲值來使其更加平滑......我可以為“燈光”完成此操作以及其他性能增強和標題,但它只會讓“燈”在很多時候停止閃爍(這不會成為一個很好的演示),而且標題中的“電動”下劃線也不會那麼有趣。

在這個例子中,所有這些「改進」都不是理想的,但對於正常的應用程式...你可以讓它處理很多事情。如果您確實需要更多東西,那麼在這種情況下請使用其他解決方案。

結論

將流添加到您的武器庫中......它可能不是萬能的解決方案,但有一天它肯定會派上用場。

如果你想用它做點什麼並且需要幫助,那麼…也許可以打電話給我。 =P

以上是Chunk-Busters:不要跨越溪流!的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn