Reputation: 14435
I'm using the following setup in my React Native app:
connect
from react-redux
to fire an action, let's say of type MY_ACTION
. The action creator is a function which sits on the GUI's props (connect
puts it there). So, the GUI calls this.props.sendMyAction()
to dispatch.MY_ACTION
and invokes a background process every time a MY_ACTION
is dispatched: function* myActionWatcher() {
yield takeEvery(MY_ACTION, function*() {
yield spawn(backgroundSaga);
});
}
This seems to work. However, the JS thread seems to return to the GUI only when the background saga has completed:
this.props.sendMyAction();
console.info('Sent action!'); // <---- only called after backgroundSaga finishes
This seems strange, given that spawn
is exactly the kind of forking mechanism that forks the saga in an asynchronous way, i.e. such that the parent saga does not have to wait until it's finished.
What am I missing?
Upvotes: 0
Views: 1188
Reputation: 84912
What does backgroundSaga do? If it starts off with a synchronous block of code, that code will have to finish executing before control can return back to the parent saga, or to redux, or to your component.
When you call yield spawn(backgroundSaga)
, you create a detached saga and begin running that saga. The saga will run until it yields
something asynchronous (usually a promise). The fact that the child saga is detached means it's possible for the parent saga to finish even if the child saga hasn't, but the parent still needs to wait for the child to yield
. Only once the child saga yields does control return back to the parent saga, and then back to redux, and then back to your react component.
For example, suppose i have the following:
function* myActionWatcher() {
yield takeEvery(MY_ACTION, function*() {
console.log('PARENT: about to spawn');
yield spawn(backgroundSaga);
console.log('PARENT: after spawn');
});
}
function* backgroundSaga() {
console.log('CHILD: starting saga');
const state = yield select();
console.log('CHILD: after select');
yield Promise.resolve();
console.log('CHILD: after resolve');
}
The order of logs for this will be:
In other words, the child starts running immediately, and anything synchronous it does (including synchronous yields to redux-saga for information) run to completion. Then once it reaches something asynchronous control returns to the parent, and the child will resume later.
Upvotes: 1