Saga can't take action from one of its forks if the child dispatches synchronously inside the fork call
See original GitHub issueIn my Electron library I’m using the store to communicate between sagas. Everything worked fine in 0.9.5
, but after updating to 0.10.0
it broke. After some time trying to find out the problem it looks to me that takeEvery
does not always work.
I haven’t been able to replicate the problem in a simple example so far, though I was able to find out a specific moment in the library when it brakes. I put
an action to a store (which works and can be seen in devtools), but the other saga that listens for that action with takeEvery
isn’t triggered. Keep in mind that the put
is in a nested forked saga function.
I’ve tried to change all the fork
s to spawn
s since there were changes to the fork model, but that didn’t help. What did help and fixed the problem was to put yield delay(0)
before every put
.
Issue Analytics
- State:
- Created 7 years ago
- Comments:19 (7 by maintainers)
Top GitHub Comments
@MichalBures Thanks for sharing this. I think there is an error on the Fix of 0.10.1. I’ll check
@joonhyublee I dont think what we’ve here is a ‘basic case’. Let me explain.
After switching redux-saga to a promise-less version, execution of non-blocking effects (Effects which execute and resume the Saga synchronously in the same stack frame just like a synchronous function call) no longer caused the Saga to sleep. Instead Saga resume asap as the function call returns and thus cause it not to miss events dispatched sync. For example if you do
As you see, a Saga doing only non-blocking calls will execute its effect fully inside the stack frame of the 1st dispatch, when the 1st dispatch returns, the Saga is already ready to take the 2nd. So as long as your flow of actions is unidirectional Saga can’t miss an event (not talking of 0.10.1 b/c as I said there is an issue with the fix to fork)
There is one ‘edge case’, Suppose you have this fork/call chain
parent take(action) -> parent fork(child) -> … -> child(n) put(action)
So here we have a cycle on the action flow. Even with that the parent Saga should have no problem with taking the action from one of its child. Except on an ‘edge case’ (of the former ‘edge case’):
If all the call/fork chain is executing only non-bloking Effects, then the whole fork/call chain will execute as a single function call in the same stack frame. We’ll have something like this
parent…child…
take(action)… … action is dispatched from outside fork(child)… fork Effect executed: begin of fn call …put(action)… child dispatch: fork han’t yet returned … put returns … fork returns take(action)… Saga resumes
So the parent execute a normal function call and waiting for it to return. Except in the case of bounded recursion, I dont think (sync) cycles are basic cases on any kind of context I’m aware of.
The only way to fix the above is by making the nested put async. In other words by scheduling the nested put for execution later. And by later I mean until the function call to fork(child) terminate and the parent executes its next take effect (assuming the parent doing only non-blocking) (And from this POV, I don’t consider Promise.resolve solution a hacky solution)
Prior to 0.10.0. the scheduling was done using
Promise.then
. which delayed all the puts to the next microtasks. But there was an issue with this solution: Promise.then was delaying the execution ‘too much’ (to the next microtask). It means the Saga doing put (here child) can miss any consecutive action dispatched on the current task.This is why I implemented the solution in #235. My impl. delayed the nested puts only the time other Sagas are ready to take it. But the impl. of 0.10.0 handled only the cases of puts within puts not puts within forks. 0.10.1 attempts to fix this (but as I said the impl. of fix of 0.10.1 is incorrect, I’ll have ‘to fix the fix’)
Fundamentally the re-entrant lock solution in #235 is somewhat similar to the actionChannel. But instead of queuing the actions we’re queuing the functions dispatching those actions.
@yelouafi Published as issue #413