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 毫秒的延遲時間過後,且 task1
和 task2
完成其業務為止。
舉例來說,如果 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
的本體):取消它表示取消目前的 Effectdelay(1000)
- 其他仍然待處理的同時執行任務。例如,在我們的範例中為
task2
。
- 主要任務(
call(fetchAll)
本身會產生一個錯誤,此錯誤將會在main
的catch
本體中被捕捉
請注意我們只能在 main
內部,從 call(fetchAll)
中捕捉錯誤,這是因為我們使用的是攔截呼叫。而且我們無法直接從 fetchAll
中捕捉錯誤。這是經驗法則,你無法捕捉到同時執行的錯誤。附加的同時執行失敗將導致產生同時執行的父項中止(就像在並行 Effect內部無法捕捉錯誤一樣,只能從外部攔截並行 Effect 並捕捉錯誤)。
取消
取消一個 Saga 會導致取消
主要任務 這表示取消 Saga 遭阻擋的目前效應
仍執行中所有附加的分支
待進行中
分離的分支(使用 spawn
)
分離的分支位於自己的執行內容。父代不會等待分離的分支終止。已執行任務未捕捉到的錯誤不會冒泡至父代。而且取消父代不會自動取消分離的分支(你需要明確取消它們)。
簡而言之,分離的分支如同使用 middleware.run
API 直接啟動的根 Saga。
待進行中