Benjamin
Benjamin

Reputation: 571

Is it possible to call store.dispatch synchronously in redux-saga?

Is it possible to call store.dispatch synchronously in redux-saga, that is, return after the saga which takes its action finished running. For example, the following code

const { createStore, applyMiddleware } =require('redux')
const createSagaMiddleware =require('redux-saga').default
const { takeEvery ,take,fork,put}=require('redux-saga/effects') 
const {delay} =require('redux-saga')
const sagaMiddleware = createSagaMiddleware()
const reducer=(state=[],action)=>{return [...state,action.type];}
const store = createStore(
    reducer,
    applyMiddleware(sagaMiddleware)
)
function* takeSaga() {
    const action=yield take('testTake')
    yield delay(1000);
    console.log('from takeSaga')
}
sagaMiddleware.run(takeSaga)
const main=async ()=>{
    store.dispatch({type: 'testTake'})
    console.log("from main");
}
main();

outputs

from main
from takeSaga

That is, the store.dispatch({type: 'testTake'}) returns after takeSaga executed the yield delay(1000) statement. What I want to archive is that the store.dispatch({type: 'testTake'}) returns only after the takeSaga has finished its last statement console.log('from takeSaga'), so that the expected output should be

from takeSaga
from main

Is that possible? I have tried await store.dispatch({type: 'testTake'}) but without success.

Upvotes: 1

Views: 2996

Answers (1)

markerikson
markerikson

Reputation: 67459

To my knowledge, no, that's not possible.

store.dispatch() is, by itself, 100% synchronous. When you call it, it runs your reducer, runs any subscription callbacks, and returns.

Middleware can change that, by intercepting actions and delaying them in some way. But, if a middleware intercepts and delays an action, the original dispatch() call will still return, because this particular event loop needs to run to completion.

In your example, what happens is:

  • The saga middleware sees the action, and calls next(action), eventually reaching the real store.dispatch
  • The store calls the root reducer
  • The store calls all subscriber functions
  • store.dispatch returns
  • The call to next(action) returns, and the saga middleware continues executing
  • The saga middleware begins running any sagas that need to know about this action
  • Your saga begins to run, and yields a delay() effect, telling the middleware it wants to wait before it continues. The yield statement pauses your saga.
  • The middleware sees that no other sagas care about this action, and returns
  • A second later, the timeout expires, and the middleware tries to resume running your saga

So, the delay() effect causes the middleware to finish up its current work and return. You can't make the event loop block for a second with that.

Upvotes: 1

Related Questions