admin管理员组

文章数量:1391008

I am making multiple calls with Promise.

My API endpoints to fetch are:

See the code

export function getTeamsStats(league, team, type) {
  return function(dispatch) {

    const url = ";;
    let dates = ["2019-08-30", "2019-09-30", "2019-10-30"];
    const getAllData = (dates, i) => {
      return Promise.allSettled(dates.map(x => url + '/' + 357 + '/' + 5 + '/' + x).map(fetchData));
    }

    const fetchData = (URL) => {
      return axios
        .get(URL)
        .then(res => {
          const {
            matchsPlayed: { total: teamsTotalMatchsPlayed},
          } = res.data.api.statistics.matchs;

          const matchsPlayed = teamsTotalMatchsPlayed;

          dispatch(receivedTeamsStat(matchsPlayed, type));
        })
        .catch(e => {
          console.log(e);
        });
    }

    getAllData(dates).then(resp=>{console.log(resp)}).catch(e=>{console.log(e)})

  }
}

Then in my ponent, I put in an array the matches played from this specific team ( Sao Paulo in this example ) from the initial date 30-8-2019 to 30-10-2019

    const [dataHomeTeam, setDataHomeTeam] = useState([]);

      useEffect(() => {
    
         if (!team.matchsPlayed) {
           return ;
         }
    
         setDataHomeTeam(prev =>
           prev.concat([
             {
               matches: team.matchsPlayed,
             }
           ])
         );
    
       },[team.matchsPlayed]);

console.log('Data Array', dataHomeTeam);

The problem is that normally the in the first render of the page I have the right order of the matches made from 30-8-2019 to 30-10-2019

See the console log image

But sometimes not, see here

So the question is, how can I make sure that the Promise is returning me the right order of the requests?

I am using Promise.allSettled, the multiple asynchronous tasks that are not dependent on one another to plete successfully, but the order is not always right.

I am making multiple calls with Promise.

My API endpoints to fetch are:

  1. https://www.api-football./demo/v2/statistics/357/5/2019-08-30
  2. https://www.api-football./demo/v2/statistics/357/5/2019-09-30
  3. https://www.api-football./demo/v2/statistics/357/5/2019-10-30

See the code

export function getTeamsStats(league, team, type) {
  return function(dispatch) {

    const url = "https://www.api-football./demo/v2/statistics";
    let dates = ["2019-08-30", "2019-09-30", "2019-10-30"];
    const getAllData = (dates, i) => {
      return Promise.allSettled(dates.map(x => url + '/' + 357 + '/' + 5 + '/' + x).map(fetchData));
    }

    const fetchData = (URL) => {
      return axios
        .get(URL)
        .then(res => {
          const {
            matchsPlayed: { total: teamsTotalMatchsPlayed},
          } = res.data.api.statistics.matchs;

          const matchsPlayed = teamsTotalMatchsPlayed;

          dispatch(receivedTeamsStat(matchsPlayed, type));
        })
        .catch(e => {
          console.log(e);
        });
    }

    getAllData(dates).then(resp=>{console.log(resp)}).catch(e=>{console.log(e)})

  }
}

Then in my ponent, I put in an array the matches played from this specific team ( Sao Paulo in this example ) from the initial date 30-8-2019 to 30-10-2019

    const [dataHomeTeam, setDataHomeTeam] = useState([]);

      useEffect(() => {
    
         if (!team.matchsPlayed) {
           return ;
         }
    
         setDataHomeTeam(prev =>
           prev.concat([
             {
               matches: team.matchsPlayed,
             }
           ])
         );
    
       },[team.matchsPlayed]);

console.log('Data Array', dataHomeTeam);

The problem is that normally the in the first render of the page I have the right order of the matches made from 30-8-2019 to 30-10-2019

See the console log image

But sometimes not, see here

So the question is, how can I make sure that the Promise is returning me the right order of the requests?

I am using Promise.allSettled, the multiple asynchronous tasks that are not dependent on one another to plete successfully, but the order is not always right.

Share Improve this question edited Sep 15, 2020 at 9:06 Lidor shimoni 931 silver badge9 bronze badges asked May 26, 2020 at 21:28 Koala7Koala7 1,4248 gold badges43 silver badges88 bronze badges 7
  • Have you tried Promise.all? – goto Commented Sep 8, 2020 at 11:20
  • Yes, no difference – Koala7 Commented Sep 8, 2020 at 11:23
  • Now that I am looking at it closely it you're not really returning anything from the fetchData promise but you're doing some sort of dispatch, which is unclear where that's ing from, so I guess the issue you're seeing is that these dispatches are not triggered in order (which they shouldn't unless you get lucky), but if you were to call getAllData which returns a Promise.all where fetchData returns the final result inside then (instead of calling dispatch), then you'd get the right behavior. – goto Commented Sep 8, 2020 at 11:26
  • Do you mind to write the answer with a code example to better understand? – Koala7 Commented Sep 8, 2020 at 11:31
  • 1 It's unclear how you're using the getTeamsStats function inside your ponent. – goto Commented Sep 8, 2020 at 11:34
 |  Show 2 more ments

5 Answers 5

Reset to default 5 +100

This should be resolved via Promise.all because as described in documentation

Returned values will be in order of the Promises passed, regardless of pletion order.

The problem you have is that you are setting your state by calling dispatch for each promise based on it's pletion and since promises can finish at any given time (e.g. 3rd request could finish first because they are sent at the same time), dispatch will be called and your state management will set that as first response (that's why you "sometimes" get such behaviour, because it's up to the network which one will finish first).

This would mean that either you should change your dispatch to receive array of already finished promises, or call dispatch one-by-one once they are finished which is shown in code below - resolve all and dispatch in order:

export function getTeamsStats(league, team, type) {
    return function (dispatch) {
    const url = "https://www.api-football./demo/v2/statistics";
    let dates = ["2019-08-30", "2019-09-30", "2019-10-30"];
    const getAllData = (dates, i) => {
        return Promise.all(dates.map(x => url + '/' + 357 + '/' + 5 + '/' + x).map(fetchData));
    }

    const fetchData = (URL) => {
        return axios
            .get(URL)
            .then(res => {
                const {
                    matchsPlayed: { total: teamsTotalMatchsPlayed },
                } = res.data.api.statistics.matchs;

                return teamsTotalMatchsPlayed;
            })
            .catch(e => {
                console.log(e);
            });
    }

    getAllData(dates).then(resp => {
        console.log(resp)

        // 'resp' here are all 'teamsTotalMatchsPlayed' in correct order (here I mean order of call, not promise pletion)
        // so just dispatch them in order
        resp.map(matchsPlayed => receivedTeamsStat(matchsPlayed, type));            
    }).catch(e => { 
        console.log(e) 
    })

   }
}

Please note that I've maybe made some syntax error, but you get the idea.

The ideal way of achieving your requirment is by using Promise.all(). It is because of two main reasons,

  • To maintain the return value in the order of the Promises passed, regardless of pletion order.

Returned values will be in order of the Promises passed, regardless of pletion order.

Refer to the Return value section in the link

  • To reject the returned promise (short circuit) if any of the promises in the iterable are rejected.

This is also important as well. We don't need to wait for all the asynchronous iterable promises to be resolved/rejected, if the first iterable promise of fetchData rejects we can short circuit and reject the returned promise.

On the other hand Promise.allSettled(),

Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the oute of each promise.

It also doesn't maintain the order in the returned promise.

Promise.allSettled() method never short-circuits. Always Promise fulfilled, never rejected.

Refer to the following parison table between Promise.all() and Promise.allSettled(),

<script src="https://gist.github./Seralahthan/9934ba2bd185a8ccfbdd8e4b3523ea23.js"></script>

How, you have done,

function updateUI(value) {
    console.log(value);
    // Do something to update the UI.
}

// Note that order of resolution of Promises is 2, 1, 3
const promise1 = new Promise((resolve) => setTimeout(resolve, 200, 1)).then(updateUI);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 2)).then(updateUI);
const promise3 = new Promise((resolve) => setTimeout(resolve, 300, 3)).then(updateUI);
const promises = [promise1, promise2, promise3];

Promise.allSettled(promises).
  then((value) => console.log('Nothing to do here', value));

// Output: 2, 1, 3

// Here we update the UI as soon as the result is obtained. As a result, the UI is also updated in the
// order in which the promise was resolved.

In other words, we wait not only for the network call but we wait for both the network call and the UI update to plete for each id which is not you want.

How you should have done instead,

// Note that order of resolution of Promises is 2, 1, 3 (Same as previous)

const promise1 = new Promise((resolve) => setTimeout(resolve, 200, 1));
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 2));
const promise3 = new Promise((resolve) => setTimeout(resolve, 300, 3));
const promises = [promise1, promise2, promise3];


Promise.allSettled(promises).
  then((results) => results.forEach((result) => updateUI(result.value)));

// Output: 1, 2, 3

// Here, we wait for all the network requests to plete and then loop through the results and update the UI.
// This ensures that the result is in order.

If you don't want to wait for all Promises to resolve and want to update the UI as soon as one resolves and still maintain order, then you need to pass the position of the element in the array and then use that position to update the element in place at the given position in the array.

Hope this helps.

export function getTeamsStats(league, team, type) {
  return function (dispatch) {
    const URL = "https://www.api-football./demo/v2/statistics";
    let DATES = ["2019-08-30", "2019-09-30", "2019-10-30"];

    return Promise.all(
      DATES.map(date => axios.get(`${URL}/357/5/${date}`))
        .then(responseList => {
          responseList.map((res) => {
            const {
              matchsPlayed: { total: teamsTotalMatchsPlayed },
            } = res.data.api.statistics.matchs;

            const matchsPlayed = teamsTotalMatchsPlayed;

            dispatch(receivedTeamsStat(matchsPlayed, type));
          });
        })
        .catch((e) => console.log(e))
    );
  };
}

Promise.all preserves's the order. You should wait for all the api promise's to resolve first, before action dipatching.

Helpful Article: Promise.all: Order of resolved values

本文标签: javascriptReact Promise asynchronous tasks order not correctStack Overflow