跳到主要內容

redux-saga 的派生模式

redux-saga 中,你可以動態派生使用 2 種效果在背景執行的任務

  • fork 用於建立已附加派生
  • spawn 用於建立已分離派生

已附加派生 (使用 fork)

已附加派生保持附加在其父項上,以下為規則

完結

  • Saga 僅在以下情況下終止
    • 它終止其本身的指令
    • 所有已附加派生本身終止

舉例來說,我們假設有以下內容

import { fork, call, put, delay } from 'redux-saga/effects'
import api from './somewhere/api' // app specific
import { receiveData } from './somewhere/actions' // app specific

function* fetchAll() {
const task1 = yield fork(fetchResource, 'users')
const task2 = yield fork(fetchResource, 'comments')
yield delay(1000)
}

function* fetchResource(resource) {
const {data} = yield call(api.fetch, resource)
yield put(receiveData(data))
}

function* main() {
yield call(fetchAll)
}

call(fetchAll) 將在以下情況下終止

  • fetchAll 本身終止,這表示所有 3 項效果執行完畢。由於 fork 效果不會封鎖,因此任務將在 delay(1000) 上封鎖

  • 2 個同時執行的任務終止,亦即在擷取所需資源並執行對應的 receiveData 動作後

因此,整個任務將封鎖到 1,000 毫秒的延遲時間過後, task1task2 完成其業務為止。

舉例來說,如果 1,000 毫秒的延遲時間過去了,而這 2 個任務尚未完成,那麼 fetchAll 仍會等到所有同時執行的任務執行完畢後,才終止整個任務。

專心的讀者可能已經注意到 fetchAll saga 可使用並行 Effect 重寫

function* fetchAll() {
yield all([
call(fetchResource, 'users'), // task1
call(fetchResource, 'comments'), // task2,
delay(1000)
])
}

事實上,附加的同時執行具有與並行 Effect 相同的語意

  • 我們並行執行任務
  • 父項任務會在所有啟動的任務終止後終止

這也適用於所有其他語意(錯誤和取消傳播)。你可以視附加的同時執行為動態並行 Effect,進而了解它的行為。

錯誤傳播

根據相同的類比,讓我們詳細探討並行 Effect 如何處理錯誤

例如,假設我們有這個 Effect

yield all([
call(fetchResource, 'users'),
call(fetchResource, 'comments'),
delay(1000)
])

只要 3 個子 Effect 中的任何一個產生錯誤,上述 Effect 便會失敗。此外,未捕捉的錯誤會導致並行 Effect 取消所有其他待處理 Effect。例如,如果 call(fetchResource, 'users') 產生未捕捉的錯誤,那麼並行 Effect 會取消其他 2 個任務(如果它們仍處於待處理狀態),然後中止自身,並顯示呼叫失敗中所產生的相同錯誤。

對於附加的同時執行而言,情況類似,如果發生以下情況,Saga 便會中止

  • 其說明文字本體產生錯誤

  • 其附加的同時執行之一產生未捕捉的錯誤

因此,在先前的範例中

//... imports

function* fetchAll() {
const task1 = yield fork(fetchResource, 'users')
const task2 = yield fork(fetchResource, 'comments')
yield delay(1000)
}

function* fetchResource(resource) {
const {data} = yield call(api.fetch, resource)
yield put(receiveData(data))
}

function* main() {
try {
yield call(fetchAll)
} catch (e) {
// handle fetchAll errors
}
}

例如,如果 fetchAll 封鎖在 delay(1000) Effect 上,並假設 task1 失敗,那麼整個 fetchAll 任務將會失敗,導致:

  • 取消所有其他待處理任務。這包括:

    • 主要任務fetchAll 的本體):取消它表示取消目前的 Effect delay(1000)
    • 其他仍然待處理的同時執行任務。例如,在我們的範例中為 task2
  • call(fetchAll) 本身會產生一個錯誤,此錯誤將會在 maincatch 本體中被捕捉

請注意我們只能在 main 內部,從 call(fetchAll) 中捕捉錯誤,這是因為我們使用的是攔截呼叫。而且我們無法直接從 fetchAll 中捕捉錯誤。這是經驗法則,你無法捕捉到同時執行的錯誤。附加的同時執行失敗將導致產生同時執行的父項中止(就像在並行 Effect內部無法捕捉錯誤一樣,只能從外部攔截並行 Effect 並捕捉錯誤)。

取消

取消一個 Saga 會導致取消

  • 主要任務 這表示取消 Saga 遭阻擋的目前效應

  • 仍執行中所有附加的分支

待進行中

分離的分支(使用 spawn

分離的分支位於自己的執行內容。父代不會等待分離的分支終止。已執行任務未捕捉到的錯誤不會冒泡至父代。而且取消父代不會自動取消分離的分支(你需要明確取消它們)。

簡而言之,分離的分支如同使用 middleware.run API 直接啟動的根 Saga。

待進行中