[英]How to wait for all dynamic number of forks to complete with redux-saga?
[英]How to wait for another action in redux-saga
我有一些 sagas 可能會完成,然后put
另一個動作放入商店。
有些 sagas 應該只在其他 sagas 執行后才執行:它們必須阻塞,或者等到另一個 sagas 完成。
總結如下:
export function* authorize(action) {
const { clientId } = action.data;
const response = yield call(apiAuthorize, clientId);
// Redux reducer picks this up and sets a token in storage.
yield put({ type: AUTHORIZE_SUCCEEDED, data: response.data.data });
}
export function* fetchMessages(action) {
console.log(action);
const { timelineId } = action.data;
// how can we block this until either `token` is set (getToken returns non-null)
// or until AUTHORIZE_SUCCEEDED is sent?
// The token set by AUTHORIZED_SUCCEEDED is read from the storage.
// This will be null untill the AUTHORIZE_SUCCEEDED is handled by redux.
// When null, the api-call will return a 401 so we want to block untill we
// have the token.
const token = yield select(getToken);
const response = yield call(apiFetchMessages, token);
yield put({ type: MESSAGES_REQUEST_SUCCEEDED, data: response.data.data });
}
export default function* appSaga() {
yield takeEvery(AUTHORIZE_REQUESTED, authorize);
yield takeEvery(MESSAGES_REQUESTED, fetchMessages);
}
我試圖在 sagas 之間保持盡可能少的耦合,因此向我展示了一種在函數之外實現這一點的方法的獎勵積分。
請注意,這是一個簡化版本。 實際上,有幾個這樣的fetchMessages
可能會被觸發,所有這些都應該等到 AUTHORIZE_SUCCEEDED 進來。
我可以在fetchMessage()
function 中添加一個循環,但這感覺很惡心。 我對Javascript、Redux、Saga或者generator函數不是很熟悉,所以也許這種感覺是完全錯誤的。 我也不確定如何使用 sagas 的 yield/select 等超時運行循環。
while (true) {
const token = yield setTimeout(() => select(getToken), 1000);
if (!!token) {
break;
}
});
另一個有效但笨拙的技巧是在 401 上重試 fetchMessages api 調用。
try {
const response = yield call(apiFetchMessages, token);
yield put({ type: MESSAGES_REQUEST_SUCCEEDED, data: response.data.data });
} catch (error) {
if (error.request.status === 401) {
yield put({ type: MESSAGES_REQUESTED, data: { blockId } });
} else {
throw error;
}
}
傳奇中是否有 API 或 function ? 這是一個正確的模式,還是我阻止一個動作直到另一個動作完成的想法一開始是錯誤的?
從更耦合,但更簡單的解決方案開始——而不是在循環中使用延遲等待,您可以使用take
來等待AUTHORIZE_SUCCEEDED
操作:
export function* fetchMessages(action) {
const { timelineId } = action.data;
// the cycle might not be needed if you are sure the
// AUTHORIZE_SUCCEEDED action is always dispatched with a valid token
let token;
while (true) {
token = yield select(getToken);
if (token) break;
yield take(AUTHORIZE_SUCCEEDED);
}
const response = yield call(apiFetchMessages, token);
yield put({ type: MESSAGES_REQUEST_SUCCEEDED, data: response.data.data });
}
為了讓它不那么笨重,你可以把它抽象成它自己的傳奇:
export function* getTokenSaga() {
let token;
while (true) {
token = yield select(getToken);
if (token) break;
yield take(AUTHORIZE_SUCCEEDED);
}
return token;
}
export function* fetchMessages(action) {
const { timelineId } = action.data;
const token = yield call(getTokenSaga);
const response = yield call(apiFetchMessages, token);
yield put({ type: MESSAGES_REQUEST_SUCCEEDED, data: response.data.data });
}
另一種解決方法是包裝 fetching 方法:
export function* fetchWithToken(fetchFn, ...params) {
let token;
while (true) {
token = yield select(getToken);
if (token) break;
yield take(AUTHORIZE_SUCCEEDED);
}
return yield call(fetchFn, token, ...params);
}
export function* fetchMessages(action) {
const { timelineId } = action.data;
const response = yield call(fetchWithToken, apiFetchMessages);
yield put({ type: MESSAGES_REQUEST_SUCCEEDED, data: response.data.data });
}
可能解決此問題的完全不同的方法是更改您的應用程序的架構,以確保在您獲得令牌之前不會調度諸如MESSAGES_REQUESTED
之類的獲取操作 - 例如,在您獲得令牌之前顯示加載,然后才允許 rest請求附加數據的應用程序。
在這種情況下,您可以修改fetch
方法本身以獲取令牌,因為它始終可用:
const loadData = (endpoint, payload) => {
const token = getTokenSelector(store.getState())
return fetch(endpoint, payload).then(...);
}
const apiFetchMessages = () => {
return loadData('/messages');
}
export function* fetchMessages(action) {
const { timelineId } = action.data;
const response = yield call(apiFetchMessages);
yield put({ type: MESSAGES_REQUEST_SUCCEEDED, data: response.data.data });
}
如果在您分派動作的地方不可能進行這樣的更改,那么我還有另一種方法可以考慮如何確保令牌始終可用而不修改fetchMessages
傳奇本身,那就是使用緩沖其他動作actionChannel
效果,直到您擁有令牌 - 這可能會變得更加復雜,因為您需要考慮在以下情況下要緩沖什么:
export default function* appSaga() {
// we buffer all fetching actions
const channel = yield actionChannel([MESSAGES_REQUESTED, FOO_REQUESTED]);
// then we block the saga until AUTHORIZE_REQUESTED is dispatched and processed
const action = yield take(AUTHORIZE_REQUESTED);
yield call(authorize, action);
// There is multiple ways to process the buffer, for example
// we can simply redispatch the actions once we started
// listening for them using the `takeEvery` effect
yield takeEvery(MESSAGES_REQUESTED, fetchMessages);
yield takeEvery(FOO_REQUESTED, fetchFoo);
while (const action = yield take(channel)) {
yield put(action);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.