javascript - 像 Redux-Saga 這樣的框架能在不使用生成器的情況下達到相同的效果嗎?如果不能,為什麼?
我正在設計一個業務邏輯庫的 API。它的目標和方法類似於 Redux-Saga——管理中心存儲的副作用和變更傳播,同時消除任何對 Redux 或 React 的依賴。
有人能詳細說明使用 redux-saga 時使用生成器的具體好處是什麼,而不是例如提供裝飾器來處理異步函數(這樣可以攔截呼叫參數和返回值並使它們可以被中介軟體使用)嗎?我需要決定使用哪種策略。
Redux-Saga 使用以 * 宣告並使用 yield 定義業務邏輯的同步生成器函數。這與由以 async 宣告並使用 await 的基於 Promise 的非同步函數組成的副作用代碼形成對比。
在 https://github.com/redux-saga/redux-saga/issues/987 中,Redux-Saga 維護者 Mateusz Burzyński 表示,「Redux-Saga 不能重寫為 async/await。」他接著說,「作為效果的解釋者,Saga使我們能夠完全控制何時以及如何解決這些效果”,並且“我們根本無法使用生成器以外的任何東西來實現我們的目標,因為只有生成器才能為我們提供所需的靈活性。
不過,我相信包裝非同步呼叫也可以讓人攔截非同步呼叫被解決的方式和時間。我問自己,他是什麼意思?
到目前為止,我已經將我的邏輯層的『sagas』模型化為基於生成器的 Redux-Saga 方法,我對此相當滿意。然而,到目前為止開發者的反饋基本上是『你為什麼使用 yield 而不是 await?』(這來自非 redux-saga 用戶,他們可能也會抱怨 redux-saga)。
他們的觀點是,使用 yield 會讓那些覺得生成器相比 async/await 代碼更像陌生魔法的開發者感到疏離,所以我發現自己不得不為這種方法辯護,有時我也會感到困難。
基於 await 或 yield 的函數有很多相似之處。在每種情況下,步驟都可以嚴格按順序執行,它們可以委派給其他函數,它們在等待下一個事件時會“暫停”,並且根據它們在順序過程中的「進度」保持隱含狀態,而不需要定義任何額外的狀態來管理這一點。我發現很難清楚地表達哪些是無法通過 await 表達而只能通過 yield 表達的,以此來證明我最初將我的 API 與 redux-saga 和生成器對齊的合理性。對此我能說什麼來為它辯護嗎?
在底層,我的框架中的原語已經提供了基於異步的實現,用於在存儲中編輯和追蹤變更(消除了 Redux reducers 和 redux-saga 中間件的需求)以及事件隊列(滿足了類似 channel 和 takeLatest 的 action 消費模式需求)。然而,我所編寫的異步操作又被再次包裝在『Actions』中,以便在生成器層呈現它們。這讓我懷疑與直接使用異步函數調用我的異步層相比,生成器層是否真的帶來了任何好處。我是不是忽略了什麼基本概念?
我開始懷疑自己,並想了解其他人對於為什麼 Redux-Saga 使用 yield 的看法,或者相比於僅僅在異步調用周圍寫封裝,通過生成器方法可以獲得什麼特定的能力。
到目前為止,我想到兩個主要原因,但這看起來很薄弱,因為 yield 可能會疏遠許多主流開發者,而且假設 async 可以提供同樣的好處。
- 同步生成器代碼使得編寫業務邏輯測試變得更容易,也使得問題案例更容易記錄以進行調試。你可以模擬各種事件和副作用而無需使用模擬(請參見 redux-saga-test-plan),因為你可以攔截每一條指令並替換所有出現的值!話雖如此,對於 JavaScript 測試作者而言,模擬仍然是一種非常成熟且熟悉的技術。此外,如果框架引入了用於攔截應用核心異步函數的封裝,這種攔截在任何情況下都可以用於模擬和日誌記錄。
- 強迫開發者通過同步生成器來編寫邏輯,會製造一個人為障礙,把它與非同步代碼隔離開來。你甚至無法在 saga 中使用 async 關鍵字。也許這會讓人們寫出更好的業務邏輯分離?話雖如此,如果我們接受“狂野西部”風格的非同步代碼作為合法的起點,我們可以逐步引入 saga 的強大功能,只需對選定的非同步調用進行包裝以便攔截,直到達到我們實際需要的攔截級別。例如,我們的流程可以阻塞在
await actionMatching()或await selectorChange()上,以便在另一個“流程”中被通知到另一個調用的啟動或完成,或者注意到狀態轉換。這不需要通過生成器提供事件來讓流程阻塞在yield takeLatest()上。
是否有一個基本的限制,意味著我需要使用生成器來實現 Redux-Saga 的功能?
答案#1
根據這個函式庫的開發者,Redux Saga 無法改寫成使用 async/await——或許更準確地說,依我理解,對於這種函式庫的開發者來說,這將是如此繁重的工作,以至於永遠不可能發生。
從圖書館(redux-saga)的觀點來看,async/await 對生成器就像是一個年輕、笨的弟弟,眨眼 你可以用生成器做的事情,比用 async/await 多得多。-- 來源評論
和
redux-saga無法改寫成 async/await — 那使用的是 Promises,且用它們來協調「平行」任務要困難得多。這也意味著語義上的重大改變,也就是事情實際運作的方式會不同。例如selecteffect 是同步的 — 無法用 Promises 達成。
Saga 作為效果的解釋器,賦予我們對這些效果的解析方式和時間的完全控制 + 效果本身只是簡單的對象,容易比較且可以被監控,而這在使用 Promise 時則更難做到。-- 來源評論