React Redux 中介軟體思想遇見 Web Worker 的靈感(附demo)

LucasHC發表於2017-08-15

寫在最前

原文首發於作者的知乎專欄:React Redux 中介軟體思想遇見 Web Worker 的靈感(附demo),感興趣的同學可以知乎關注,進行交流。

熟悉 React 技術棧的同學,想必對 Redux 資料流框架並不陌生。其倡導的單向資料流等思想獨樹一幟,雖然樣板程式碼會有一定程度上的增多,但是對於開發效率和除錯效率的提高是顯著的。同時還帶來了很多諸如 “時間旅行”,“ undo/redo ” 等黑魔法。

其實這還只是表象。如果你深入去了解 Redux 的設計理念,探索中介軟體奧祕,玩轉高階 reducer 等等,迎接你的就會是另一扇門。透過它,函數語言程式設計思想之光傾斜如注。

思想背景

但是隨著這個 web app 複雜度的提升,資料計算量壓力徒增,你所設計的 Reducer 變得臃腫不堪。好吧,我們可以拆分 Reducer 使得程式碼看上去更加舒服。可是計算量呢?也許有一些“夢魘”,瓶頸般永遠無法消除。

冥冥之中,“各種處理計算既然註定在同一時空,那麼能否永遠平行?”

曾幾何時,你是否聽說過 JS 單執行緒非同步?聽說過瀏覽器卡頓或卡死?聽說過 60 fps?

其實一個很嚴峻的事實是:根據 60 fps 計算,每一幀留給我們 JS 執行的時間為 16ms(甚至更少)。那麼一旦當 Reducer 計算時間過長,必然會影響瀏覽器渲染。

多執行緒思路

關於瀏覽器主執行緒、render queue、event loop、call stack 等內容,本文不再複述,因為裡面的知識完全都夠寫一本書了。假定讀者對其有一二認知,那麼你也不難理解我們即將登場的救星—— Web Worker!

我們先來簡單認識一下 web worker:

2008 年 W3C 制定出第一個 HTML5 草案開始,HTML5 承載了越來越多嶄新的特性和功能。其中,最重要的一個便是對多執行緒的支援。在 HTML5 中提出了工作執行緒(Web Worker)的概念,並且規範出 Web Worker 的三大主要特徵:

  • 能夠長時間執行(響應);
  • 理想的啟動效能;
  • 以及理想的記憶體消耗。

Work 執行緒可以執行任務而不干擾使用者介面。

於是,腦洞大開,能否將我們的 Redux Reducer 計算狀態部分放進 Worker 執行緒中處理呢?

答案是肯定的。
那麼要如何實施呢?

我們先來看一下經典的 Redux workflow,如下圖:

redux 流程圖
redux 流程圖

如果要接入 Web Work,那麼我們改動流程圖如下:

redux + worker 流程圖
redux + worker 流程圖

具體實現和一個demo

當然,有了思路,還需要在實戰中演練。

我使用 “N-皇后問題” 模擬大型計算,並且實現的 demo 中可以任意設定 n 值,增加計算耗時。
如果你不理解此演算法也沒有關係,只需要知道N-皇后問題這個演算法的計算耗時很長,且和 n 值相關:n 越大,計算成本越大。

除了一個極其耗時的計算,頁面中還執行這麼幾個模組,來實現複雜的渲染邏輯操作:

  • 一個實時每16毫秒,顯示計數(每秒增加1)的 blinker 模組;
  • 一個定時每500毫秒,更新背景顏色的 counter 模組;
  • 一個永久往復運動的 slider 模組;
  • 一個每16毫秒翻轉5度的 spinner 模組

頁面過程
頁面過程

這些模組都定時頻繁地更新 dom 樣式,進行大量複雜的渲染計算。正常情況下,由於 JS 主執行緒進行N-皇后計算,這些渲染過程都將被卡頓。

同時,我設定“N-皇后問題”的 n 值,來觀察在計算時這些模組的表現(是否卡頓)。在不開啟 Work 執行緒的情況下,n 設定為13時,有 gif 圖,左半部分:

off.gif
off.gif

我們非常清晰地看到:由於瀏覽器 call stack 進行 n=13 的皇后問題計算,而無法“按時”渲染,所以造成了這幾個模組的卡頓,這些模組都無法更新狀態。在這個卡頓過程中,使用者的任何事件(如點選,敲鍵盤等)都無法被瀏覽器響應。這就是使用者體會到的“慢”!

如果我把 n 值設定的大與13呢,比如24?
千萬不要這麼做!因為你的瀏覽器會被卡死!我使用 Mac Pro 8G 記憶體情況下,設定到14,瀏覽器就無法響應了。

在開啟 Work 執行緒時,請參考上 gif 圖右半部分,幾個模組的渲染絲毫不受影響。完美達到了我們的目的。

因為 Reducer 的超級耗時計算被放入 Worker 執行緒當中,所以絲毫沒有影響瀏覽器的渲染和響應。完全解決了使用者覺得“電腦慢”的問題。

看到了如此完美的對比,也許你想問 Web Worker 的相容性如何呢?

相容性
相容性

總結

其實,這篇文章的意義並不在於這個 demo 和應用。而是在啟發一種新的想法的同時,review 了很多 JS 當中關鍵概念和基本知識。比如:單執行緒非同步、宿主環境、60 fps、一個演算法等等。

更值得一提的是,如果你去深入 demo 程式碼,你更會發現 Redux 設計精妙的思想,比如我們將 Web Worker 的應用抽象出一個公共庫:Redux-Worker,幷包裝為 Redux 的中介軟體(middleware),所有 React Redux 都可以無侵入,採用中介軟體的思想使用:

import { applyWorker } from 'redux-worker';
const enhancerWithWorker = compose(
    applyMiddleware(thunk, logger),
    applyWorker(worker)
);

const store = createStore(rootReducer, {}, enhancerWithWorker);複製程式碼

當然,Redux-Worker 這個中介軟體的實現原理更是巧妙,這裡不再展開。感興趣的同學可以參考我的此專案 Github 倉庫。我 fork 了此庫原始碼,並在核心邏輯加入了中文註釋,感興趣的同學可以關注。

我的其他關於 React 文章:

Happy Coding!

PS:
作者Github倉庫知乎問答連結
歡迎各種形式交流。

相關文章