Redux: whats the redux idiom for waiting for multiple async calls?

Created on 14 Sep 2015  ·  30Comments  ·  Source: reduxjs/redux

When i initiate react component, I need to make 2 different async http calls and wait for their results.
These 2 calls will ideally put their data in 2 different reducers.

I understand the pattern for making a single async call.
However, I dont see any good samples for making multiple async calls, having them execute in parallel and wait for _both_ their results to arrive.

What is the redux way of doing this?

question

Most helpful comment

I'm assuming you use Redux Thunk middleware.

Declare some action creators:

import 'babel-core/polyfill'; // so I can use Promises
import fetch from 'isomorphic-fetch'; // so I can use fetch()

function doSomething() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING, json }),
      err => dispatch({ type: SOMETHING_FAILED, err })
    );
}

function doSomethingElse() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING_ELSE, json }),
      err => dispatch({ type: SOMETHING_ELSE_FAILED, err })
    );
}

Note how I make it a point to return a Promise at the very end by keeping the chain as return value of the function inside thunk action creators. This lets me do that:

store.dispatch(doSomething()).then(() => {
  console.log('I did something');
});

When you use Redux Thunk, dispatch returns the return value of the function you returned from the thunk action creator—in this case, the Promise.

This lets you use combinators like Promise.all() to wait for completion of several actions:

Promise.all([
  store.dispatch(doSomething()),
  store.dispatch(doSomethingElse())
]).then(() => {
  console.log('I did everything!');
});

However it is preferable to define another action creator that would act as a _composition_ of the previous action creators, and use Promise.all internally:

function doEverything() {
  return dispatch => Promise.all([
    dispatch(doSomething()),
    dispatch(doSomethingElse())
  ]);
}

Now you can just write

store.dispatch(doEverything()).then(() => {
  console.log('I did everything!');
});

Happy dispatching!

All 30 comments

I'm assuming you use Redux Thunk middleware.

Declare some action creators:

import 'babel-core/polyfill'; // so I can use Promises
import fetch from 'isomorphic-fetch'; // so I can use fetch()

function doSomething() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING, json }),
      err => dispatch({ type: SOMETHING_FAILED, err })
    );
}

function doSomethingElse() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING_ELSE, json }),
      err => dispatch({ type: SOMETHING_ELSE_FAILED, err })
    );
}

Note how I make it a point to return a Promise at the very end by keeping the chain as return value of the function inside thunk action creators. This lets me do that:

store.dispatch(doSomething()).then(() => {
  console.log('I did something');
});

When you use Redux Thunk, dispatch returns the return value of the function you returned from the thunk action creator—in this case, the Promise.

This lets you use combinators like Promise.all() to wait for completion of several actions:

Promise.all([
  store.dispatch(doSomething()),
  store.dispatch(doSomethingElse())
]).then(() => {
  console.log('I did everything!');
});

However it is preferable to define another action creator that would act as a _composition_ of the previous action creators, and use Promise.all internally:

function doEverything() {
  return dispatch => Promise.all([
    dispatch(doSomething()),
    dispatch(doSomethingElse())
  ]);
}

Now you can just write

store.dispatch(doEverything()).then(() => {
  console.log('I did everything!');
});

Happy dispatching!

@gaearon Is there any particular reason that you favor thunk over promise middleware? or advice on when to use what? Thanks.

No particular reason, I'm just more familiar with it.
Try both and use what works best for you!

Understood, thanks!

JFYI :)
screen shot 2015-09-14 at 11 06 42 am

if you want you can take a look on it https://github.com/Cron-J/redux-async-transitions

@gaearon quick question - looking at your example above, where you call:

store.dispatch(doSomething()).then(() => {
  console.log('I did something');
});

— it looks like that "I did something" block would be hit even if doSomething rejected - since you're not rethrowing the error in the doSomething chain after you catch it. Is that intentional? If not - and if you'd rethrow the error there instead - would you handle it again in the caller?

It is just an example, I don’t mean this a definitive reference, just as a way to look at it. I didn’t give it a minute of real thought.

You know how to return promises from action creators, the rest is up to you. Whether to rethrow errors, where to handle them, is all up to you. This is not really a Redux concern so my advise is as good as anyone else’s, and in this case you have much more context on what you want build and the style you prefer than I will ever have.

Cool, thanks! Figured that was the case, but wanted to make sure there wasn't some magic I was overlooking.

Since Redux is customizable, dispatch return value is not guaranteed to be expected promise.

Actually, I ran into one issue in real life since I takes redux-loop. It tweaks dispatch to return a wrapped Promise(https://github.com/raisemarketplace/redux-loop/blob/master/modules/effects.js#L38). So, we cannot depend on return value of dispatch.

Well, as Dan said above in this thread: it's _your_ app, _you_ are writing your action creators, so _you_ get to decide what they return.

@markerikson The return value of dispatch is not guaranteed to be return value of action creator, since middleware may tweak it.

But, I got a workaround. Instead of chain then on dispatch(actonCreator()), we can chain on actionCreator().

Yes, middleware can tweak things, but my point is that _you_ are setting up the middleware in your own app :)

Hi @gaearon, quick (hopefully related) question! What are your views on doing something like

function fetchDataBWhichDependsOnA() {
  return dispatch => {
    dispatch(fetchDataA())
      .then(() => {
        fetch('/api/get/data/B')
          .then(dispatch(receiveDataB(response.data)))
      })
  }
}

There are 2 nested async actions in this code snippet, they work but I'm having trouble testing it. I'm able to test fetchDataA() because it has only 1 level of asynchronous call. But when I was trying to write a test for fetchDataBWhichDependsOnA(), I couldn't retrieve the most updated values from the store in then block.

@timothytong You are not returning the promises. If you return the result from dispatch and the nested fetch call you should be able to wait in your tests until the whole chain has been resolved.

@johanneslumpe good catch, thanks mate! Now all that's left is the question of whether this is acceptable or not, dealing with data dependency with an async fetch chain?

I had the same problem and spent 30 minutes trying to come up with a solution. I'm not done this implementation, but I feel like it's on the right track.

https://github.com/Sailias/sync-thunk

My idea is that a component can request certain redux states to be populated, if they are not it should refer to a map to call the actions to populate them and chain them with then. Each action in the chain doesn't need data passed in, it can just take the data from the state as the dependant actions would have populated it.

@gaearon

Promise.all([
  store.dispatch(doSomething()),
  store.dispatch(doSomethingElse())
]).then(() => {
  console.log('I did everything!');
});

I have a question on this. Sorry if it is so basic.

In this scenario, there will be two dispatches. That means, two times the component will be rendered. But, what if component wants both data to show something meaningful?

So, in that scenario, I would like the component to render only once and that is when all the async actions are completed.

I got names of few middlewares which seem to be handling this issue like redux-batched-subscribe and redux-batched-actions. But, a little confused on the best approach.

If this is a valid scenario, what would be the suggested approach to handle this?

@jintoppy if your ask is to avoid multiple call to render a component then the Promise.all solution will not help you; because both doSomething() and doSomething() all dispatch data that would trigger state change and cause component render.

@jintoppy why do you need any middlwares to handle your situation?
What's hinder you of doing smth like that.

// action creator
function fetchAB() {
  return dispatch => {
    const fetchA = fetch( 'api/endpoint/A' );
    const fetchB = fetch( 'api/endpoint/B' );
    return Promise.all([ fetchA, fetchB ])
      .then( values => dispatch(showABAction(values)) )
      .catch( err => throw err );
  }
} 

You can just put all your request into the one action creator and fire dispatching of action only once all of them are resolved. In that case you have reducer called and thus state changed only once, so render() likely will be fired only once as well.

@apranovich: I see one problem with this approach. Now, I need to combine multiple fetch in a single action. So, if I have, let's say 10 async calls, I have to put all those in a single action. Also, what if I want to trigger one out of these 10 async calls later? In this approach, basically, all your async logic will lie in a single action.

Thank you all for the helpful information. Quick question, how can the same be achieved but instead when one fetch returns data that is required by the next fetch method. For example, there is a free weather API endpoint where I input the geolocation to get an id (first fetch) which is required for the next fetch to get the location's weather. I know this is an odd use case, but I would like to know how to handle similar situations. Thanks in advance!

Hi @gaearon, I would like to get your review on this question of mine. I have to make nested async calls to two APIs. The response from the second API is dependent on the response of the first one.

export const getApi=()=>{
        return(d)=>{
        axios({
            method:'GET',
            url:Constants.URLConst+"/UserProfile",
            headers:Constants.headers
        }).then((response)=>{
            return d({
                type:GET_API_DATA,
                response,
                axios({
                    method:'GET',
                    url:Constants.URLConst+"/UserProfileImage?enterpriseId="+response.data.ProfileData.EnterpriseId,
                    headers:Constants.headers
                }).then((response1)=>{
                    return d({
                        type:GET_PROFILE_IMAGE,
                        response1
                    })
                })
            })
        }).catch((e)=>{
            console.log("e",e);
        })
    }

}

I tried something like above, but this doesn't work.And in my project, I have to use response from both the APIs independently.
So, what is the right way of doing it?

Thanks.

@c0b41 , please check the updated question.

hey @c0b41 , this is giving syntax errors in the second axios call.

@aayushis12 , @c0b41 : this is a bug tracker, not a help forum. You should try asking this question on Stack Overflow or Reactiflux. There will be more people who will see the question and be able to help.

For those experiencing that

I did everything

is printed before all async calls are resolved, check whether you're returning a statement or an expression from your async action creators.

For example, if doSomethingElse() is declared with { } in the arrow function

function doSomethingElse() {
  return dispatch => {
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING_ELSE, json }),
      err => dispatch({ type: SOMETHING_ELSE_FAILED, err })
    );
  }
}

"I did everything" will print before doSomethingElse() resolves. Solve this by dropping { } after =>.

@gaearon one question, based on https://github.com/reduxjs/redux/issues/723#issuecomment-139927639 how would you handle it if you need to dispatch an action that waits for two actions to have been dispatched _AND_ the state to have changed because of those actions? I'm having a case in which the actions are dispatched but the action inside the then is executed _before_ the state has changed. We're using redux-thunk.

@enmanuelduran : Dan isn't an active maintainer any more - please don't ping him.

It's hard to answer your question without seeing code, but our issues really aren't intended to be a discussion forum. It would be better if you post your question on Stack Overflow - you're likely to get a better answer there, faster.

@markerikson oh, sorry, it was not my intention, created a question in stackoverflow if someone is interested: https://stackoverflow.com/questions/51846612/dispatch-an-action-after-other-specific-actions-were-dispatched-and-state-change

thanks a lot guys.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jbri7357 picture jbri7357  ·  3Comments

dmitry-zaets picture dmitry-zaets  ·  3Comments

vraa picture vraa  ·  3Comments

vslinko picture vslinko  ·  3Comments

timdorr picture timdorr  ·  3Comments