Reputation: 11870
I have a basic saga that looks like this:
const mySaga = function* () {
yield takeEvery("SOME_ACTION_REQUEST", function* (action) {
const result = yield call(makeApiCall, action.payload);
yield put({
type: "SOME_ACTION_SUCCESS",
payload: result
});
});
}
Now the problem I'm having is that if I have two "SOME_ACTION_REQUEST"
dispatched simultaneously, then I have a situation where the redux call stack looks like:
SOME_ACTION_REQUEST
SOME_ACTION_REQUEST
SOME_ACTION_SUCCESS
SOME_ACTION_SUCCESS
And this is screwing up the logic in my reducer.
What I want, is for every request to be run, but for it to wait for the previous one to have completed before it starts.
ie. so it would look like:
SOME_ACTION_REQUEST
SOME_ACTION_SUCCESS
SOME_ACTION_REQUEST
SOME_ACTION_SUCCESS
How would I achieve this?
Upvotes: 3
Views: 1737
Reputation: 11870
The actionChannel
effect can be used to achieve this.
See: https://redux-saga.js.org/docs/api/#actionchannelpattern-buffer
const mySaga = function* () {
const channel = yield actionChannel("SOME_ACTION_REQUEST");
while (true) {
const action = yield take(channel);
const result = yield call(makeApiCall, action.payload);
yield put({
type: "SOME_ACTION_SUCCESS",
payload: result
});
}
}
Explanation:
My understanding is that the actionChannel
effect is just going to route all of the incoming requests matching that pattern into a queue.
The take
effect is going to pop them off.
Putting it all in a while(true)
loop means that the actions will be popped off one at a time, and they need to wait to resolve all of the rest of the things (the API calls) before the next one can be called.
One thing to note about this pattern, is that in redux-dev-tools, the redux stack is still going to look like:
SOME_ACTION_REQUEST
SOME_ACTION_REQUEST
SOME_ACTION_SUCCESS
SOME_ACTION_SUCCESS
because the requests immediately are added to the channel and remain inactive until they're popped off.
Upvotes: 5
Reputation: 12174
You can achieve this with just two forked tasks. Like a ping-pong messaging system.
For takeEvery
always create a new forked task for every action that is received.
Something like this:
function* ping() {
while (true) {
const { payload } = yield take("SOME_ACTION_REQUEST");
yield put({
type: "DO_REQUEST",
payload
});
yield take("SOME_ACTION_SUCCESS");
}
}
function* pong() {
while (true) {
const { payload } = yield take("DO_REQUEST");
const result = yield call(makeApiCall, payload);
yield put({
type: "SOME_ACTION_SUCCESS",
payload: result
});
}
}
function* rootSaga() {
yield all([
fork(ping),
fork(pong),
]);
}
Upvotes: 0