If i have two sagas waiting for a yield take()
on the same action, is there a guarantee which saga will pick up the action first and execute its remaining logic or is random? I need to ensure the first saga executes its logic before the second saga does.
function* rootSaga() {
yield [
saga1(),
saga2()
]
}
function* saga1() {
while (true) {
//Random logic
yield take("MY_ACTION")
//Finish executing logic
}
}
function* saga2() {
while (true) {
//Random logic
yield take("MY_ACTION")
//Finish executing logic
}
}
You cannot rely here on the order of execution. When your action is dispatched, all sagas that have yielded a take
effect matching the action will be resumed at once. If an order of execution was "guaranteed", it would be an implementation detail you shouldn't rely on.
If you need your saga2
to be resumed after saga1
has executed the logic subsequent to "MY_ACTION"
, what your saga2
should really be waiting for is a different action indicating that saga1
has finished its job, rather than the first one.
function* rootSaga() {
yield [
saga1(),
saga2()
]
}
function* saga1() {
while (true) {
//Random logic
yield take("MY_ACTION")
//Finish executing logic
yield put({type: "MY_ACTION_DONE"})
}
}
function* saga2() {
while (true) {
//Random logic
yield take("MY_ACTION_DONE")
//Finish executing logic
}
}
Rather than having both run independently, you can call or fork the dependent generator from the initial one after the necessary logic has been performed.
Note there is a subtle but important difference between call
and fork
. call(saga2)
is blocking so would pause your while loop and not react to any more "MY_ACTION"
actions until saga2 has completed too, whereas fork(saga2)
is non-blocking, acting like a background task so would continue to execute in parallel to your loop resuming, so you could continue to respond to further "MY_ACTION"
.
Another thing to note is that fork
is still attached to the parent saga, so will be cancelled along with it, errors will bubble to parent from it etc. I imagine this is desirable but if you need a completely detached version that will continue to run no matter what happens to parent saga, use spawn
instead
function* rootSaga() {
yield all([
fork(saga1)
])
}
function* saga1() {
while (true) {
//Random logic
const action = yield take("MY_ACTION")
//Finish executing logic
// BLOCKING `call` saga2, passing original action as param if needed
yield call(saga2, action)
// OR NON-BLOCKING `fork` saga2
yield fork(saga2, action)
// OR NON-BLOCKING DETACHED `spawn` saga2
yield spawn(saga2, action)
}
}
function* saga2(action) {
// Do whatever dependant logic
// safe in the knowledge that saga1 has done its job already
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With