跳轉至主要內容

API 參考

Middleware API

createSagaMiddleware(options)

建立 Redux 中介軟體並連接 Sagas 至 Redux 儲存庫

  • options: Object - 傳遞至中介軟體的選項清單。目前支援選項為

    • context: Object - saga 背景的初始值。

    • sagaMonitor : SagaMonitor - 如果提供 Saga Monitor,中介軟體會將監控事件傳送至監視器。

    • onError: (error: Error, { sagaStack: string }) - 如果提供,中介軟體會使用 Saga 的未捕捉錯誤呼叫它。對於將未捕捉例外傳送至錯誤追蹤服務很有用。

    • effectMiddlewares : Function [] - 讓您可以攔截任何效果,自行解析並傳遞至下一個中介軟體。詳情範例請參閱此章節

    • 頻道:如果提供了這個頻道,中介軟體會使用這個頻道,而不是預設值 stdChannel()

    • take 以及 put 效果。

範例

以下我們會建立一個函式 configureStore,它會使用新的方法 runSaga 來增強 Store。然後在我們的 main 模組中,我們會使用這個方法來啟動應用程式的主 Saga。

configureStore.js

import createSagaMiddleware from 'redux-saga'
import reducer from './path/to/reducer'

export default function configureStore(initialState) {
// Note: passing middleware as the last argument to createStore requires redux@>=3.1.0
const sagaMiddleware = createSagaMiddleware()
return {
...createStore(reducer, initialState, applyMiddleware(/* other middleware, */sagaMiddleware)),
runSaga: sagaMiddleware.run
}
}

main.js

import configureStore from './configureStore'
import rootSaga from './sagas'
// ... other imports

const store = configureStore()
store.runSaga(rootSaga)

注意事項

查看以下內容以取得有關 sagaMiddleware.run 方法的更多資訊。

middleware.run(saga, ...args)

動態執行 saga。僅可在 applyMiddleware 階段之後使用它來執行 Saga。

  • saga:函式:產生器函式
  • args:Array<any>:提供給 saga 的論證

這個方法會傳回 任務描述符

注意事項

saga 必須是傳回 產生器物件 的函式。接著,中介軟體會反覆執行產生器,並執行所有產生的效果。

saga 也可以使用程式庫提供的各種效果來啟動其他 saga。下面所述的反覆執行程序也會套用至所有子 saga。

在第一次反覆執行中,中介軟體會呼叫 next() 方法來擷取下一個效果。接著,中介軟體會根據一下方的效果 API 來執行產生的效果。同時,產生器會暫停,直到效果執行結束。在收到執行結果後,中介軟體會呼叫產生器中的 next(result),將擷取到的結果傳遞給它作為引數。這個程序會重複執行,直到產生器正常結束或拋出某些錯誤。

如果執行結果為錯誤(各個效果建立器所指定),則會呼叫產生器的 throw(error) 方法。如果產生器函式在目前的 yield 指令附近定義了 try/catch,則 catch 區塊會由基礎產生器執行時間呼叫。執行時間也會呼叫任何對應的 finally 區塊。

如果取消 Saga(手動取消或使用所提供的效果),則中介軟體會呼叫產生器的 return() 方法。這會導致產生器直接跳到 finally 區塊。

效果建立器

注意事項

  • 以下的每個函式都會傳回純粹的 JavaScript 物件,而且不會執行任何行為。
  • 執行會由中介軟體在上面所述的反覆執行程序中進行。
  • 中介軟體會檢查每個 Effect 的描述並執行適當的動作。

take(pattern)

創建一個 Effect 描述,指示中介軟體等待 Store 上的特定動作。直到傳送與 pattern 匹配的動作,產生器才會暫停。

yield take(pattern) 的結果是一個已傳送的動作物件。

pattern 會使用下列規則進行解譯

  • 如果 take 沒有參數或 '*',則與所有傳送的動作相符(例如,take() 將與所有動作相符)

  • 如果它是函式,且 pattern(action) 為 true,則相符動作(例如,take(action => action.entities) 與所有有 (true) entities 欄位的動作相符)

    注意:如果模式函數有定義 toString,則會測試 action.typepattern.toString() 而不是。這在使用動作建立函式庫(如 redux-act 或 redux-actions)時很有用。

  • 如果是字串,動作相符的條件為 action.type === pattern(例如 take(INCREMENT_ASYNC)

  • 如果是陣列,則陣列中的每一項目都會使用上述規則進行匹配,因此支援字串和函式謂詞的混合陣列。但最常見的用例是字串陣列,這樣 action.type 會與陣列中的所有項目相符(例如 take([INCREMENT, DECREMENT]),這會匹配類型為 INCREMENTDECREMENT 的動作)。

中介軟體提供一個特殊動作 END。如果您傳送 END 動作,那麼所有因 take Effect 而受阻的 Saga 都會終止,與指定模式無關。如果終止的 Saga 仍有一些仍在執行的分岔工作,它會等到所有子工作終止後才終止該工作。

takeMaybe(pattern)

take(pattern) 相同,但不會自動在 END 動作上終止 Saga。所有因 take Effect 而受阻的 Saga 都會取得 END 物件。

注意事項

takeMaybe 的名稱來自 FP 類比 - 好像不是返回 ACTION 類型(自動處理),而是可以有 Maybe(ACTION) 類型,讓我們可以處理兩種狀況

  • 案例是 Just(ACTION)(我們有一個動作)
  • NOTHING 案例(頻道已關閉*)。也就是說,我們需要某些方法來對 END 進行映射
  • 內部所有 dispatch 的動作都會透過 stdChannel 進行,而當發生 dispatch(END) 時,它就會被關閉

take(channel)

建立一個 Effect 說明,指示中間件等待由提供的頻道發送來指定的訊息。如果頻道已經關閉,則產生器將立即終止,遵循與上方的 take(pattern) 相同的過程。

takeMaybe(channel)

take(channel) 相同,但不會自動終止一個在 END 動作上封鎖的 Saga。反而所有在一個執行中封鎖的 Saga 會取得 END 物件。請參閱 此處 的詳細資料

takeEvery(pattern, saga, ...args)

在傳送至 Store 並符合 pattern 的每個動作上產生一個 saga

  • pattern: 字串 | 陣列 | 函式 - 有關詳細資料,請參閱 take(pattern) 的文件

  • saga: 函式 - 一個產生器函式

  • args: Array<any> - 傳遞至啟動任務的引數。takeEvery 會將傳入動作新增至引數清單 (亦即動作會成為傳遞至 saga 的最後一個引數)

範例

在下列範例中,我們建立一個基本任務 fetchUser。我們使用 takeEvery,在每一個傳送的 USER_REQUESTED 動作時開始一個新的 fetchUser 任務

import { takeEvery } from `redux-saga/effects`

function* fetchUser(action) {
...
}

function* watchFetchUser() {
yield takeEvery('USER_REQUESTED', fetchUser)
}

注意事項

takeEvery 是使用 takefork 建立的高階 API。這裡是使用低階 Effect 可以如何實作助手

const takeEvery = (patternOrChannel, saga, ...args) => fork(function*() {
while (true) {
const action = yield take(patternOrChannel)
yield fork(saga, ...args.concat(action))
}
})

takeEvery 允許同時處理並行的動作。在上方的範例中,當一個 USER_REQUESTED 動作被傳送時,一個新的 fetchUser 任務會開始,即使先前的 fetchUser 仍然掛起 (例如,使用者以極快的速度連續按兩次 載入使用者 按鈕,第二次按鈕點擊將會傳送一個 USER_REQUESTED 動作,而第一次觸發的 fetchUser 尚未終止)

takeEvery 沒有處理任務的順序回應。沒有保證任務的終止順序會與其開始順序相同。若要處理順序回應,可以考慮以下的 takeLatest

takeEvery(channel, saga, ...args)

您也可以傳入一個頻道作為引數,而且行為與 takeEvery(pattern, saga, ...args) 相同。

takeLatest(pattern, saga, ...args)

在傳送至 Store 並符合 pattern 的每個動作上複製一個 saga。而且自動取消先前開始的任何 saga 任務,如果它仍然正在執行。

每次將動作發送至儲存。而如果此動作符合樣式takeLatest便會在背景中開始新的saga工作。如果saga工作先前已經開始(在前次動作發送之前),且此工作仍在執行中,則會取消該工作。

  • pattern: 字串 | 陣列 | 函式 - 有關詳細資料,請參閱 take(pattern) 的文件

  • saga: 函式 - 一個產生器函式

  • args: Array<any> - 傳遞給已啟動工作的參數。takeLatest會將接收的動作新增至參數清單中(即動作會是提供給saga 的最後一個參數)

範例

在以下範例中,我們建立一個基本工作fetchUser。我們使用takeLatest在每個發送動作USER_REQUESTED時開始一個新的fetchUser工作。由於takeLatest會取消任何先前開始的待決工作,因此我們確保使用者如果快速觸發多個連續的USER_REQUESTED動作,我們只會以最新動作作結

import { takeLatest } from `redux-saga/effects`

function* fetchUser(action) {
...
}

function* watchLastFetchUser() {
yield takeLatest('USER_REQUESTED', fetchUser)
}

注意

takeLatest是一種高階 API,建置於takefork。以下說明輔助程式如何使用低階效應實作

const takeLatest = (patternOrChannel, saga, ...args) => fork(function*() {
let lastTask
while (true) {
const action = yield take(patternOrChannel)
if (lastTask) {
yield cancel(lastTask) // cancel is no-op if the task has already terminated
}
lastTask = yield fork(saga, ...args.concat(action))
}
})

takeLatest(channel, saga, ...args)

您也可以傳入一個頻道當作參數,行為會與takeLatest(pattern, saga, ...args)相同。

takeLeading(pattern, saga, ...args)

在符合樣式的每一項發送至儲存的動作上產生一個saga。在產生一個工作後,它會封鎖直到產生的 saga 完成,然後開始再次偵測樣式

簡單來說,當takeLeading沒有執行 saga 時會偵測動作。

  • pattern: 字串 | 陣列 | 函式 - 有關詳細資料,請參閱 take(pattern) 的文件

  • saga: 函式 - 一個產生器函式

  • args: Array<any> - 傳遞給已啟動工作的參數。takeLeading會將接收的動作新增至參數清單中(即動作會是提供給saga 的最後一個參數)

範例

在以下範例中,我們建立一個基本工作fetchUser。我們使用takeLeading在每個發送動作USER_REQUESTED時開始一個新的fetchUser工作。由於takeLeading在開始後會忽略任何新到工作,因此我們確保使用者如果快速觸發多個連續的USER_REQUESTED動作,我們只會繼續執行領先動作

import { takeLeading } from `redux-saga/effects`

function* fetchUser(action) {
...
}

function* watchLastFetchUser() {
yield takeLeading('USER_REQUESTED', fetchUser)
}

注意

takeLeading是一種高階 API,建置於takecall。以下說明輔助程式如何使用低階效應實作

const takeLeading = (patternOrChannel, saga, ...args) => fork(function*() {
while (true) {
const action = yield take(patternOrChannel);
yield call(saga, ...args.concat(action));
}
})

takeLeading(頻道,劇情,...引數)

您也可以將頻道傳入,作為引數,行為與 takeLeading(模式,劇情,...引數) 相同。

put(動作)

建立效應說明,指示中間層規劃調度動作到儲存空間。因為任務佇列的其他任務可能尚在前方或仍在進行中,因此此調度可能不會立即進行。

但是,除非有其他有非同步流程的 Redux 中間層,會延遲動作的傳播,否則您預期可以在目前的堆疊框架內 (也就是在 yield put(動作) 之後程式碼的下一行) 更新儲存空間。

下游錯誤 (例如,來自還原器的錯誤) 將會顯示。

putResolve(動作)

put 相同,但效果是停滯的 (如果 dispatch 傳回承諾,它會等待承諾解除),而且會顯示來自下游的錯誤。

put(頻道,動作)

建立效應說明,指示中間層將動作放入已提供的頻道。

如果放置沒有緩衝,而是立刻由取得者使用,此效應具有停滯性。如果任一取得者發生錯誤,會從劇情中顯示回來。

call(函式,...引數)

建立效應說明,指示中間層呼叫函式 fn,其中 args 作為引數。

  • 函式: 函式 - 產生器函式或一般函式,其中一個會傳回承諾作為結果,或任何其他值。
  • 引數: 陣列<任意> - 傳遞給 fn 作為引數的值的陣列

備註

fn 可以是一般或產生器函式。

中間層會呼叫函式,並檢查結果。

結果是一個反覆運算物件,中間件會執行該生成器函式,就像它對啟動生成器所做的一樣(在啟動時傳遞給中間件)。父生成器會暫停,直到子生成器正常終止,此時父生成器會繼續執行,並帶有子生成器返回的值。或者,在子生成器中產生一些錯誤而中斷,此時父生成器內部會產生一個錯誤。

如果 fn 是一個一般函式並傳回一個承諾,中間件會暫停生成器,直到承諾解決。在承諾解決後,生成器會繼續執行,並帶有已解決的值;或者,如果承諾被拒絕,就會在生成器內部產生一個錯誤。

如果結果既不是反覆運算物件也不是承諾,中間件會立即將該值傳回 saga,以便能同步繼續執行。

當生成器內部產生一個錯誤時,如果它有一個圍繞目前 yield 指令的 try/catch 區塊,控制權就會傳遞給 catch 區塊。否則,生成器會帶有引發的錯誤而中斷,如果此生成器是由另一個生成器呼叫,該錯誤將會傳遞給呼叫生成器。

call([context, fn], ...args)

call(fn, ...args) 相同,但支援將 this 內容傳遞給 fn。這對呼叫物件方法時很有用。

call([context, fnName], ...args)

call([context, fn], ...args) 相同,但支援傳遞 fn 作為字串。這對呼叫物件方法時很有用,例如 yield call([localStorage, 'getItem'], 'redux-saga')

call({context, fn}, ...args)

call([context, fn], ...args) 相同,但支援傳遞 contextfn 為物件屬性,例如 yield call({context: localStorage, fn: localStorage.getItem}, 'redux-saga')fn 可以是字串或函式。

apply(context, fn, [args])

call([context, fn], ...args) 的別名。

cps(fn, ...args)

建立一個效果描述,指示中間件呼叫 fn 為節點樣式函式。

  • fn: 函式 - 一個節點樣式函式。例如一個接受其自訂引數、並在終止時,呼叫 fn 的額外回呼函式的函式。回呼函式接受兩個參數,第一個參數用於報告錯誤,而第二個參數用於報告成功的結果

  • args: Array<any> - 要傳遞給 fn 為自訂引數的陣列

注意事項

中介軟體會執行呼叫程式碼 fn(...arg, cb)cb 是中介軟體傳遞給 fn 的回呼。如果 fn 正常終止,則必須呼叫 cb(null, result) 以通知中介軟體有一項成功的結果。如果 fn 遇到錯誤,則必須呼叫 cb(error) 以通知中介軟體發生錯誤。

中介軟體會保持暫停狀態,直到 fn 終止。

cps([context, fn], ...args)

支援傳遞 this Context 給 fn(物件方法呼叫)

cps({context, fn}, ...args)

cps([context, fn], ...args),但支援將 contextfn 傳遞為物件的屬性。fn 可以是字串或函式。

fork(fn, ...args)

建立 Effect 說明,指示中介軟體在 fn 上執行「非封鎖呼叫」

參數

  • fn: Function - Generator 函式,或傳回 Promise 作為結果的普通函式
  • 引數: 陣列<任意> - 傳遞給 fn 作為引數的值的陣列

傳回 Task 物件。

注意事項

fork,同 call,可用來呼叫普通或 Generator 函式。但是,呼叫屬於非封鎖類型,中介軟體在等待 fn 的結果時不會暫停 Generator。相反地,一旦 fn 被呼叫,Generator 便會立刻恢復。

forkrace 同為管理 Sagas 之間競發的 Effect 主要手段。

yield fork(fn ...args) 的結果為 Task 物件。一個具有一些有用方法與屬性的物件。

所有分支任務都「附加」在其父層任務上。當父層任務終止執行自己的指令主體時,會等待所有分支任務在返回前終止。

錯誤傳播

子層任務的錯誤會自動浮現至其父層。如果任何分支任務引發未捕捉的錯誤,則父層任務會中止,且帶有子層 Error,而整個父層執行樹(也就是分支任務 + 仍然執行的父層主體所代表的「主層任務」)將會被取消。

取消分支任務將自動取消仍在執行的所有分支任務。它也會取消被取消任務阻塞的目前 Effect(如果有的話)。

如果已 fork 的工作在同步方式下失敗(即:在執行任何非同步作業前立即執行後失敗),則不會傳回工作;相反地,父工作會在盡快時間中止(因為父工作與子工作同時執行,父工作會在注意到子工作失敗後立即中止)。

要建立已分離的 fork,請改用 spawn

fork([context, fn], ...args)

支援使用 this 內容呼叫已 fork 的函式

fork({context, fn}, ...args)

fork([context, fn], ...args) 相同,但支援將 contextfn 傳遞為物件的屬性。fn 可以為字串或函式。

spawn(fn, ...args)

fork(fn, ...args) 相同,但會建立已分離的工作。已分離的工作會保留其父項目的獨立性,並會像頂層工作一樣運作。父工作不會等待已分離的工作終止才會傳回,而任何可能會影響父工作或已分離工作的事件(錯誤、取消)都是完全獨立的。

spawn([context, fn], ...args)

支援使用 this 內容建立函式

join(task)

建立效應說明,指示中介軟體等待先前已 fork 工作的結果。

  • task: Task - 前一個 fork 傳回的 工作 物件

註解

join 會解決為已加入工作相同的結果(成功或錯誤)。如果已加入的工作已取消,則取消也會傳播到執行加入效應的 Saga。同樣地,這些加入項的任何可能呼叫者也會被取消。

join([...tasks])

建立效應說明,指示中介軟體等待先前已 fork 工作的結果。

  • tasks: Array<Task> - 工作 是先前 fork 傳回的物件

註解

它會將工作陣列放入加入效應中,大致上會等同於 yield tasks.map(t => join(t))

cancel(task)

建立效應說明,指示中介軟體取消之前已 fork 的工作。

  • task: Task - 前一個 fork 傳回的 工作 物件

註解

要取消正在執行的任務,中間件會在底層產生器物件上呼叫 return。這將取消任務中的目前 Effect 並跳到 finally 區塊(如有定義)中。

在 finally 區塊中,你可以執行任何清理邏輯,或發送某些動作以保持儲存在一致的狀態(例如,在 ajax 要求被取消時,將 spinner 的狀態重設為 false)。你可以透過發出 yield cancelled(),來檢查 Saga 是否已被取消。

取消會向下傳遞到子屬 Saga。取消任務時,中間件也會取消目前的 Effect(任務目前被鎖住的地方)。如果目前的 Effect 是呼叫其他 Saga,也會將其取消。取消 Saga 時,所有附加的 fork(使用 yield fork() 分叉的 Saga)都會被取消。這表示取消會有效地影響屬於取消任務的整個執行流程樹。

cancel 是一個非同步 Effect,也就是執行它的 Saga 在執行取消動作後會立即繼續。

對於會回傳 Promise 結果的函式,你可以附加上 [CANCEL] 到 Promise 中,以插入你自己的取消邏輯。

以下範例顯示如何將取消邏輯附加到 Promise 結果

import { CANCEL } from 'redux-saga'
import { fork, cancel } from 'redux-saga/effects'

function myApi() {
const promise = myXhr(...)

promise[CANCEL] = () => myXhr.abort()
return promise
}

function* mySaga() {

const task = yield fork(myApi)

// ... later
// will call promise[CANCEL] on the result of myApi
yield cancel(task)
}

redux-saga 會自動使用他們的 abort 方法來取消 jqXHR 物件。

cancel([...tasks])

建立 Effect 說明,指示中間件取消先前分叉的任務。

  • tasks: Array<Task> - 工作 是先前 fork 傳回的物件

註解

它會將任務陣列包裝在 取消效果 中,粗略地等於 yield tasks.map(t => cancel(t))

cancel()

建立 Effect 說明,指示中間件取消已傳回的任務(自我取消)。它允許在 finally 區塊中,重新使用類似解構器的邏輯,以做為外部(cancel(task))和自我(cancel())取消。

範例

function* deleteRecord({ payload }) {
try {
const { confirm, deny } = yield call(prompt);
if (confirm) {
yield put(actions.deleteRecord.confirmed())
}
if (deny) {
yield cancel()
}
} catch(e) {
// handle failure
} finally {
if (yield cancelled()) {
// shared cancellation logic
yield put(actions.deleteRecord.cancel(payload))
}
}
}

select(selector, ...args)

建立一個效果,指示中間件在目前的儲存狀態中呼叫提供的選擇器(亦即回傳 selector(getState(), ...args) 的結果)。

  • selector: Function - 函式 (state, ...args) => args。它會取得目前的狀態,並可選擇取得一些自訂參數,以及回傳目前儲存狀態的一部份區塊

  • args: Array<any> - 可選自訂參數,除 getState 之外,會傳遞給選擇器。

如果未加上自訂參數呼叫 select(亦即 yield select()),則效果會解析整個狀態(與呼叫 getState() 的結果相同)。

請注意,當動作傳送至儲存時,中間件會先將動作轉發至還原器,然後再通知 Sagas。這表示當你查詢儲存的狀態時,取得的狀態會是動作套用之後 的狀態。不過,此行為僅在所有後續中間件同步呼叫 next(action) 的情況下有保證。如果後續任一個中間件非同步呼叫 next(action)(情況不太常見,但有可能),則 Sagas 會取得動作套用之前 的狀態。因此建議檢閱每個後續中間件的來源,以確保它同步呼叫 next(action),否則應確保 redux-saga 是呼叫鏈中的最後一個中間件。

註解

最理想的是,Saga 應該具備自主性,且不應依賴儲存的狀態。這樣可以輕易修改狀態實作,而不會影響 Saga 程式碼。一個 Saga 最好只能依賴其本身的內部控制狀態(如果可能)。不過,有時可能會發現 Saga 查詢狀態會比自己維護所需資料來得方便(例如,當 Saga 重複呼叫部分還原器的邏輯,以計算儲存已計算完成的狀態時)。

舉例來說,假設我們的應用程式有以下這個狀態形狀

state = {
cart: {...}
}

我們可以建立一個選擇器,即一個知道如何從狀態中擷取 cart 資料的函式

./selectors

export const getCart = state => state.cart

然後,我們可以使用 select 作用,從 Saga 內部使用該選擇器

./sagas.js

import { take, fork, select } from 'redux-saga/effects'
import { getCart } from './selectors'

function* checkout() {
// query the state using the exported selector
const cart = yield select(getCart)

// ... call some API endpoint then dispatch a success/error action
}

export default function* rootSaga() {
while (true) {
yield take('CHECKOUT_REQUEST')
yield fork(checkout)
}
}

透過使用 select(getCart)checkout 可以直接取得所需資訊。Saga 只會與 getCart 選擇器進行結合。如果有很多 Saga(或 React 元件)需要存取 cart 片段,它們都會和同一個函式 getCart 結合。且如果我們現在變更狀態形狀,我們只需要更新 getCart

actionChannel(pattern, [buffer])

建立一個作用,指示中間件使用事件通道將符合 pattern 的動作排入佇列。視情況而定,你可以提供一個緩衝區來控制佇列動作的緩衝。

  • pattern: - 參閱 take(pattern) 的 API
  • buffer: Buffer - Buffer 物件

範例

以下程式碼會建立一個通道,用於緩衝所有 USER_REQUEST 動作。請注意,即使 Saga 可能停在 call 作用上,所有在它停止期間發生的動作都會自動緩衝。這會讓 Saga 一次執行一個 API 呼叫

import { actionChannel, call } from 'redux-saga/effects'
import api from '...'

function* takeOneAtMost() {
const chan = yield actionChannel('USER_REQUEST')
while (true) {
const {payload} = yield take(chan)
yield call(api.getUser, payload)
}
}

flush(channel)

建立一個效應來指示middleware衝出頻道中所有快取的項目。快取的項目將傳回至saga,在必要時可以使用這些項目。

  • 頻道: 頻道 - 頻道 物件。

範例


function* saga() {
const chan = yield actionChannel('ACTION')

try {
while (true) {
const action = yield take(chan)
// ...
}
} finally {
const actions = yield flush(chan)
// ...
}

}

cancelled()

建立一個效應來指示middleware回傳此產生器是否已取消。通常於 finally 區塊使用此效應,來執行取消特定程式碼

範例


function* saga() {
try {
// ...
} finally {
if (yield cancelled()) {
// logic that should execute only on Cancellation
}
// logic that should execute in all situations (e.g. closing a channel)
}
}

setContext(props)

建立一個效應來指示middleware更新它自有的內容。此效應會擴充saga的內容,而非取代它。

getContext(prop)

建立一個效應來指示middleware回傳saga內容的特定屬性。

delay(ms, [val])

傳回一個效應描述子,用於封鎖執行 ms 毫秒,並傳回 val 值。

throttle(ms, pattern, saga, ...args)

在與商店配送的動作上衍生出一個saga,符合pattern。衍生任務後,仍接受傳入的動作,放入基礎buffer中,最多保留1個(最新一個),但同時暫停衍生新任務 ms 毫秒(因此它的名稱為throttle)。此目的在於處理任務時,忽略特定時間段的傳入動作。

  • ms:數字 - 動作開始處理後,時間視窗的長度(以毫秒計),期間動作將被忽略

  • pattern: 字串 | 陣列 | 函式 - 有關詳細資料,請參閱 take(pattern) 的文件

  • saga: 函式 - 一個產生器函式

  • args:陣列<任意> - 輸往已開始任務的引數。throttle會將傳入的動作加入引數清單中(即,動作會是提供給saga的最後一個引數)

範例

在下列範例中,我們建立一個基本任務fetchAutocomplete。我們使用throttle於已配送的FETCH_AUTOCOMPLETE動作上,開始一個新的fetchAutocomplete任務。然而,由於throttle會忽略連續的FETCH_AUTOCOMPLETE一段時間,我們確保使用者不會以要求淹沒我們的伺服器。

import { call, put, throttle } from `redux-saga/effects`

function* fetchAutocomplete(action) {
const autocompleteProposals = yield call(Api.fetchAutocomplete, action.text)
yield put({type: 'FETCHED_AUTOCOMPLETE_PROPOSALS', proposals: autocompleteProposals})
}

function* throttleAutocomplete() {
yield throttle(1000, 'FETCH_AUTOCOMPLETE', fetchAutocomplete)
}

備註

throttle是一個使用takeforkactionChannel建立的高階API。以下是如何使用低階效應實作此輔助程式

const throttle = (ms, pattern, task, ...args) => fork(function*() {
const throttleChannel = yield actionChannel(pattern, buffers.sliding(1))

while (true) {
const action = yield take(throttleChannel)
yield fork(task, ...args, action)
yield delay(ms)
}
})

throttle(ms, channel, saga, ...args)

你也可以處理一個頻道作為參數,而行為會和 throttle(ms, pattern, saga, ..args) 相同。

debounce(ms, pattern, saga, ...args)

`saga` 再傳送至與`pattern` 相符的儲存庫時產生的 action。在停止產生`pattern` action `ms` 毫秒後,會呼叫 Saga。此目的在於防止在 action 沉澱之前呼叫 Saga.

  • ms: 數字 - 定義上次觸發`pattern` action 後,傳遞給 saga 前應經過多少毫秒。

  • pattern: 字串 | 陣列 | 函式 - 有關詳細資料,請參閱 take(pattern) 的文件

  • saga: 函式 - 一個產生器函式

  • args: 陣列<任何> - 傳遞給開始的任務的參數。`debounce` 會將傳入的 action 加到參數清單中 (換句話說:action 會是傳遞到 saga 的最後一個參數)

範例

在以下範例中,我們建立了一個基本任務 `fetchAutocomplete`。我們使用 `debounce` 延遲呼叫 `fetchAutocomplete` saga,直到我們停止接收任何 `FETCH_AUTOCOMPLETE` 事件至少 `1000` 毫秒。

import { call, put, debounce } from `redux-saga/effects`

function* fetchAutocomplete(action) {
const autocompleteProposals = yield call(Api.fetchAutocomplete, action.text)
yield put({type: 'FETCHED_AUTOCOMPLETE_PROPOSALS', proposals: autocompleteProposals})
}

function* debounceAutocomplete() {
yield debounce(1000, 'FETCH_AUTOCOMPLETE', fetchAutocomplete)
}

注意事項

debounce 是使用 `take`、`delay`、`race` 和 `fork` 構建的高階 API。以下是使用低階 Effects 來實作此協助程式的方法

const debounce = (ms, pattern, task, ...args) => fork(function*() {
while (true) {
let action = yield take(pattern)

while (true) {
const { debounced, latestAction } = yield race({
debounced: delay(ms),
latestAction: take(pattern)
})

if (debounced) {
yield fork(task, ...args, action)
break
}

action = latestAction
}
}
})

debounce(ms, channel, saga, ...args)

你也可以處理一個頻道作為參數,而行為會和 debounce(ms, pattern, saga, ..args) 相同。

retry(maxTries, delay, fn, ...args)

建立一個 Effect 描述,指示中間件以 `args` 作為參數呼叫函數 `fn`。如果失敗,則在 `delay` 毫秒後再次呼叫,如果嘗試次數 < `maxTries`。

  • maxTries: 數字 - 最大呼叫次數。
  • delay: 數字 - `fn` 呼叫之間的毫秒計時視窗長度。
  • fn: 函數 - 產生器函數、或一般函數,傳回 Promise 作為結果,或任何其他值。
  • 引數: 陣列<任意> - 傳遞給 fn 作為引數的值的陣列

範例

在以下範例中,我們建立了一個基本任務 `retrySaga`。我們使用 `retry` 嘗試以 10 秒間隔呼叫我們的 API 3 次。如果 `request` 第一次失敗,`retry` 會再呼叫 `request` 一次,而呼叫次數少於 3 次。

import { put, retry } from 'redux-saga/effects'
import { request } from 'some-api';

function* retrySaga(data) {
try {
const SECOND = 1000
const response = yield retry(3, 10 * SECOND, request, data)
yield put({ type: 'REQUEST_SUCCESS', payload: response })
} catch(error) {
yield put({ type: 'REQUEST_FAIL', payload: { error } })
}
}

注意事項

retry 是使用 `delay` 和 `call` 構建的高階 API。 以下是使用低階 Effects 來實作此協助程式的方法

效應組合器

race(effects)

建立效應描述,指示中間件執行多個效應的賽跑(這類似於 Promise.race([...]) 的行為)。

effects: 物件 - 格式為 {標籤: 效應, ...} 的字典物件

範例

以下範例在兩個效應中執行賽跑

  1. 呼叫會傳回 Promise 的函數 fetchUsers
  2. CANCEL_FETCH 動作可能會最終在 Store 上傳送
import { take, call, race } from `redux-saga/effects`
import fetchUsers from './path/to/fetchUsers'

function* fetchUsersSaga() {
const { response, cancel } = yield race({
response: call(fetchUsers),
cancel: take(CANCEL_FETCH)
})
}

如果 call(fetchUsers) 最早解析,race 的結果將會是一個已單鍵編碼物件 {response: result},其中 resultfetchUsers 解析的結果。

如果 call(fetchUsers) 最早被否決,race 會拋出否決原因。

如果 fetchUsers 完成前,類型為 CANCEL_FETCH 的動作在 Store 上傳送,結果將會是單鍵編碼物件 {cancel: action},其中 action 是已傳送的動作。

備註

解析 race 時,中間件會自動取消所有落敗的效應。

race([...effects])(使用陣列)

race(effects) 相同,但是允許輸入效應陣列。

範例

以下範例在兩個效應中執行賽跑

  1. 呼叫會傳回 Promise 的函數 fetchUsers
  2. CANCEL_FETCH 動作可能會最終在 Store 上傳送
import { take, call, race } from `redux-saga/effects`
import fetchUsers from './path/to/fetchUsers'

function* fetchUsersSaga() {
const [response, cancel] = yield race([
call(fetchUsers),
take(CANCEL_FETCH)
])
}

如果 call(fetchUsers) 最早解析,response 會是 fetchUsers 的結果,而 cancel 會是 undefined

如果 call(fetchUsers) 最早被否決,race 會拋出否決原因。

如果 fetchUsers 完成前,類型為 CANCEL_FETCH 的動作在 Store 上傳送,response 會是 undefined,而 cancel 會是已傳送的動作。

all([...effects]) - 平行效應

建立效應描述,指示中間件平行執行多個效應,並等待所有效應完成。它相當於標準 Promise#all 的 API。

範例

以下範例平行執行兩個阻斷呼叫

import { fetchCustomers, fetchProducts } from './path/to/api'
import { all, call } from `redux-saga/effects`

function* mySaga() {
const [customers, products] = yield all([
call(fetchCustomers),
call(fetchProducts)
])
}

all(effects)

all([...effects]) 相同,但是允許輸入已標籤的字典物件,就像 race(effects)

  • effects: 物件 - 格式為 {標籤: 效應, ...} 的字典物件

範例

以下範例平行執行兩個阻斷呼叫

import { fetchCustomers, fetchProducts } from './path/to/api'
import { all, call } from `redux-saga/effects`

function* mySaga() {
const { customers, products } = yield all({
customers: call(fetchCustomers),
products: call(fetchProducts)
})
}

注意事項

執行平行效果時,中介軟體會暫停產生器,直到發生下列其中一項結果

  • 所有效果都順利完成:使用包含所有效果結果的陣列,繼續產生器。

  • 在所有效果都完成之前,其中一個效果遭到拒絕:在產生器中擲回拒絕錯誤。

介面

任務

任務介面指定使用 forkmiddleware.runrunSaga 執行傳奇後獲得的結果。

方法傳回值
task.isRunning()如果任務尚未傳回或擲回錯誤,則為 true
task.isCancelled()如果任務已取消,則為 true
task.result()任務傳回值。如果任務仍正在執行,則為 `undefined`
task.error()任務擲回錯誤。如果任務仍正在執行,則為 `undefined`
task.toPromise()一個 Promise,可能是
  • 以任務的傳回值解析
  • 以任務的擲回錯誤拒絕
task.cancel()取消任務(如果仍正在執行)

頻道

頻道是使用在任務間傳送和接收訊息的物件。來自傳送者的訊息會先排隊,直到有興趣的接收者要求訊息,而註冊的接收者也會排隊,直到訊息可用為止。

每個頻道都有基本緩衝區,用於定義緩衝策略(固定大小、捨棄、滑動)

頻道介面定義了 3 種方法:takeputclose

Channel.take(callback): 用於註冊接收器。將使用下列規則解析 take

  • 如果頻道已緩衝訊息,則將使用基本緩衝區中的下一個訊息 (使用 buffer.take()) 呼叫 callback
  • 如果頻道已關閉且沒有緩衝訊息,則會使用 END 呼叫 callback
  • 否則 callback 將會排隊,直到有訊息放入頻道中

Channel.put(message): 用於在緩衝區中放入訊息。將根據下列規則處理 put

  • 如果頻道已關閉,則 put 將沒有任何作用。
  • 如果有待處理的接收器,則以訊息呼叫最舊的接收器。
  • 否則將訊息放入基本緩衝區中

Channel.flush(callback): 用於從頻道擷取所有緩衝訊息。將根據下列規則解析 flush

  • 如果頻道已關閉且沒有緩衝訊息,則會使用 END 呼叫 callback
  • 否則,會使用所有緩衝訊息呼叫 callback

Channel.close(): 會關閉頻道,表示將不再允許任何 put。所有待處理的接收器都將使用 END 呼叫。

緩衝區

用於為通道實現緩衝策略。緩衝區介面定義 3 個方法:isEmptyputtake

  • isEmpty():如果緩衝區沒有訊息,則傳回 true。每當註冊新的接收者時,通道就會呼叫這個方法
  • put(message):用於將新訊息放入緩衝區。注意,緩衝區可以選擇不儲存訊息(例如,捨棄緩衝區可以捨棄超過給定限制的任何新訊息)
  • take() 用於擷取任何緩衝訊息。注意,這個方法的行為必須與 isEmpty 相符

Saga 監視器

由中間件用於傳送監控事件。中間件實際上會傳送 6 個事件

  • 當啟動根部 saga(透過 runSagasagaMiddleware.run)時,中間件會呼叫 sagaMonitor.rootSagaStarted

  • 當效應被觸發(透過 yield someEffect)時,中間件會呼叫 sagaMonitor.effectTriggered

  • 如果效應以成功解決,則中間件會呼叫 sagaMonitor.effectResolved

  • 如果效應被錯誤拒絕,則中間件會呼叫 sagaMonitor.effectRejected

  • 如果效應被取消,則中間件會呼叫 sagaMonitor.effectCancelled

  • 最後,當 Redux 動作被傳送時,中間件會呼叫 sagaMonitor.actionDispatched

以下是每個方法的簽章

  • sagaMonitor.rootSagaStarted(options):其中選項是具有下列欄位的物件

    • effectId:數字 - 分配給這個根部 saga 執行之唯一 ID

    • saga:函式 - 開始執行的產生函式

    • args:陣列 - 傳遞給產生函式的引數

  • effectTriggered(options)

    • effectId:數字 - 分配給讓步效應的唯一 ID

    • parentEffectId:數字 - 父項效應的 ID。在 raceparallel 效應的情況下,讓步內的所有效應都有一個直接的 race/parallel 效應作為父項。在頂層效應的情況下,父項會是包含的 Saga

    • label:字串 - 在 race/all 效應的情況下,所有子項效應會被分配為標籤,其中物件傳遞給 race/all 的對應鍵

    • effect:物件 - 讓步效應本身

  • effectResolved(effectId, result)

    • effectId:數字 - 讓步效應的 ID

    • result:任意 - 效應成功解析的結果。在 forkspawn 效應的情況下,結果將會是 Task 物件。

  • effectRejected(effectId, error)

    • effectId:數字 - 讓步效應的 ID

    • error:任意 - 效應拒絕所引發的錯誤

  • effectCancelled(effectId)

    • effectId:數字 - 讓步效應的 ID
  • actionDispatched(action)

    • action:物件——已發派的 Redux 動作。如果動作是由 Saga 發派的,則該動作會有屬性 SAGA\_ACTION,設為 true(SAGA\_ACTION 可從 @redux-saga/symbols 匯入)。

外部 API


runSaga(options, saga, ...args)

允許在 Redux 中介軟體環境之外啟動 Saga。如果你想將 Saga 連接到外部的輸入/輸出,而非儲存動作,這個功能就很好用。

runSaga 會傳回一個 Task 物件。就像 fork 作用傳回的物件一樣。

  • options: 物件——目前支援的選項為

  • saga: 函式 - 一個產生器函式

  • args: Array<any>——提供給 saga 的引數

注意事項

{channel, dispatch} 用於完成 takeput 作用。這會定義 Saga 的輸入/輸出介面。

channel 用於完成 take(PATTERN) 作用。每當頻道上有東西被放入時,它就會通知所有待處理的內部偵聽器。如果 Saga 是因 take 作用而被封鎖,而且此作用範例符合目前進來的輸入,則會使用該輸入來繼續該 Saga。

dispatch 用於完成 put 作用。每當 Saga 發出 yield put(output) 時,就會使用輸出喚起 dispatch

你可以在 此處 找到範例,說明如何使用此 API。

工具

channel([buffer])

一種工廠方法,可以用於建立頻道。你可以選擇傳遞緩衝區,來控制頻道如何緩衝訊息。

如果未提供緩衝區,通道則預設會將收到的訊息排隊,最多 10 個,直到有註冊的感興趣使用者。預設緩衝會以先進先出策略傳遞訊息:新的使用者會收到緩衝區中最舊的訊息。

eventChannel(訂閱, [緩衝區])

建立通道,該通道將使用 訂閱 方法訂閱事件來源。事件來源的傳入事件將排隊在通道中,直到有註冊感興趣的使用者。

  • 訂閱:函數 用於訂閱基礎事件來源。此函數必須傳回解除訂閱函數,用於終止訂閱。

  • 緩衝區:緩衝區 選擇性的緩衝區物件,用於在這頻道中暫存訊息。如果未提供,訊息將不會緩衝在這個頻道中。

要通知頻道事件來源已終止,您可以使用 END 來通知提供的訂閱者。

範例

在以下範例中,我們建立一個將訂閱 setInterval 的事件通道。

const countdown = (secs) => {
return eventChannel(emitter => {
const iv = setInterval(() => {
console.log('countdown', secs)
secs -= 1
if (secs > 0) {
emitter(secs)
} else {
emitter(END)
clearInterval(iv)
console.log('countdown terminated')
}
}, 1000);
return () => {
clearInterval(iv)
console.log('countdown cancelled')
}
}
)
}

緩衝區

提供一些常見的緩衝區。

  • 緩衝區.none():無任何緩衝,若沒有任何待處理的使用者,則新訊息將會遺失。

  • 緩衝區.fixed(上限):新訊息將緩存在 上限。溢位將引發錯誤。省略 上限 值,將會將上限設為 10。

  • 緩衝區.expanding(初始大小):與 fixed 類似,但溢位將導致緩衝區動態擴展。

  • 緩衝區.dropping(上限):與 fixed 類似,但溢位將靜默地捨棄訊息。

  • 緩衝區.sliding(上限):與 fixed 類似,但溢位將會將新訊息插入結尾,並捨棄緩衝區中最舊的訊息。

cloneableGenerator(產生器函數)

取用產生器函數 (function*),並回傳產生器函數。所有從這個函數實例化的產生器將可以複製。僅供測試用途。

範例

當您想測試故事的不同分支,而不需要重新執行導致該分支的動作時,這會很有用。

import { cloneableGenerator } from '@redux-saga/testing-utils';

function* oddOrEven() {
// some stuff are done here
yield 1;
yield 2;
yield 3;

const userInput = yield 'enter a number';
if (userInput % 2 === 0) {
yield 'even';
} else {
yield 'odd'
}
}

test('my oddOrEven saga', assert => {
const data = {};
data.gen = cloneableGenerator(oddOrEven)();

assert.equal(
data.gen.next().value,
1,
'it should yield 1'
);

assert.equal(
data.gen.next().value,
2,
'it should yield 2'
);

assert.equal(
data.gen.next().value,
3,
'it should yield 3'
);

assert.equal(
data.gen.next().value,
'enter a number',
'it should ask for a number'
);

assert.test('even number is given', a => {
// we make a clone of the generator before giving the number;
data.clone = data.gen.clone();

a.equal(
data.gen.next(2).value,
'even',
'it should yield "even"'
);

a.equal(
data.gen.next().done,
true,
'it should be done'
);

a.end();
});

assert.test('odd number is given', a => {

a.equal(
data.clone.next(1).value,
'odd',
'it should yield "odd"'
);

a.equal(
data.clone.next().done,
true,
'it should be done'
);

a.end();
});

assert.end();
});

createMockTask()

回傳一個模擬任務的物件。僅供測試用途。 (請參閱任務取消文件,以取得更多資訊。 )

秘笈

封鎖 / 非封鎖

名稱封鎖
takeEvery
takeLatest
takeLeading
throttle
debounce
重試
取用
取用(頻道)有時(請見 API 參考)
大概取用
放進
放入解決
放進(頻道, 動作)
呼叫
應用
CPS
分叉
生成
加入
取消
選擇
動作頻道
清空
已取消
競賽
延遲
所有當陣列或物件中有阻擋效應時,會進行阻擋