JavaScript Multiple API calls with fetch in chain and Promise.all multiple calls with no answer,multiple calls with facetime,multiple

I'm making an API call in my function to fetch some data. Then I need to make multiple API calls for each item of the data returned, based on a specific value from the first call. I have a problem when I'm rendering state, the values added from multiple promises are not present during rendering.

So far I have something like this:

fetch(url)
.then(resp => resp.json())
.then(data => {
  //do some calculations and populate new array and return it
})
.then(data => {
   const newData = [...data];
   Promise.all(functionWhichReturnsArrayOfPromises).then(el => {
      // more calculation
      newData.push(el.someValue);
   })
   return newData;
})
.then(data => this.setState({data: data}))

Function which returns array of promises looks something like that:

fetchMoreData(arr) {
   const promises = arr.map(el => {
      return fetch(someUrl)
      .then(data => data.json())
      .then(data => data)
   })
   return promises;
}

I think that my chaining the Promise.all inside another promise is not good, is there a recommended and more elegant way to do this ?

Answer:1

You are correct in saying that your approach is not good, and here's why:

.then(data => {
   const newData = [...data];
   Promise.all(fetchMoreData(newData)).then(el => {
      // more calculation
      newData.push(el.someValue);
   })
   return newData;
})

return newData happens before you reach newData.push(el.someValue), and since they reference the same array, that means you're calling setState() and passing an array which is mutated asynchronously, independent of when your component re-renders.

Essentially, you have created a race-condition that will make your component state indeterminate because it is based on whether the fetch() operations complete before or after the React framework decides to re-render the component.


To solve this, there are two options that come to mind, so choose whichever is more readable to you, or consistent with your coding style, but first let's address a small refactor to make your helper function more canonical.

An asynchronous function should prefer to return a promise to an array, not an array of promises:

fetchMoreData(arr) {
   const promises = arr.map(el =>
     fetch(someUrl)
       .then(res => res.json())
   );

   return Promise.all(promises);
}

With that in mind, let's continue to the two solutions:

Nest (and return) the promise chain dependent on the previous scope

fetch(url)
  .then(res => res.json())
  .then(data => {
    // do some calculations and populate new array and return it
  })
  .then(array => {
    // nest the promise chain
    return fetchMoreData(array).then(result => {
      // more calculations dependent on array from previous scope
      result.forEach(el => {
        array.push(el.someValue);
      });

      // pass array along
      return array;
    });
  })
  .then(data => {
    this.setState({ data });
  });

Notice that we return fetchMoreData(array).then(...), and within the nested continuation, also return array. This will allow array to be passed along to data in the next chain.

Pass the dependency from the previous scope through a flattened promise chain

fetch(url)
  .then(res => res.json())
  .then(data => {
    // do some calculations and populate new array and return it
  })
  .then(array => {
    const promise = fetchMoreData(array);
    // pass the dependency along
    return Promise.all([array, promise]);
  })
  // destructure dependencies
  .then(([array, result]) => {
    // more calculations dependent on array from previous scope
    result.forEach(el => {
      array.push(el.someValue);
    });

    // pass array along
    return array;
  })
  .then(data => {
    this.setState({ data });
  });

Here, we encapsulate the dependency in another Promise.all() and pass both the array and promise along to the next flattened chain, which we then use array destructuring syntax to separate out again in the callback parameter. From there, we perform the additional calculations and then pass the array along to the final chain.

Answer:2

I'm trying my hand at web dev after a few years of pause and CORS seems to have gone off the deep end. why does <script type="module" src="./index.js"></script> result in a CORS error? ...

I'm trying my hand at web dev after a few years of pause and CORS seems to have gone off the deep end. why does <script type="module" src="./index.js"></script> result in a CORS error? ...

I have a function that is supposed to capitalize the first letter of each word. I have a neat solution with a .map; however, for sake of practice, I'm trying to tackle it using a for loop. Here is ...

I have a function that is supposed to capitalize the first letter of each word. I have a neat solution with a .map; however, for sake of practice, I'm trying to tackle it using a for loop. Here is ...

  1. capitalize first letter javascript
  2. capitalize first letter python
  3. capitalize first letter excel
  4. capitalize first letter of each word
  5. capitalize first letter java
  6. capitalize first letter css
  7. capitalize first letter c#
  8. capitalize first letter r
  9. capitalize first letter of each word javascript
  10. capitalize first letter google sheets
  11. capitalize first letter php
  12. capitalize first letter after colon
  13. capitalize first letter of each word python
  14. capitalize first letter ruby
  15. capitalize first letter sql
  16. capitalize first letter in quote
  17. capitalize first letter of each word java
  18. capitalize first letter word
  19. capitalize first letter of word javascript
  20. capitalize first letter javascript es6

I´m just beginning to learn Html5 canvas, the main idea is to make an image move through multiple lines, creating paths, the total distance of the lines should be 10km, so i will be using 10 lines of ...

I´m just beginning to learn Html5 canvas, the main idea is to make an image move through multiple lines, creating paths, the total distance of the lines should be 10km, so i will be using 10 lines of ...

  1. android draw multiple paths
  2. d3 draw multiple paths

In python I can use any object as a dictionary key (7, "hello", None): e.g. myDict = {} x = someObject() myDict[x] = "world" But I cannot use an unbound identifier. To give an example of an another ...

In python I can use any object as a dictionary key (7, "hello", None): e.g. myDict = {} x = someObject() myDict[x] = "world" But I cannot use an unbound identifier. To give an example of an another ...

  1. does python allow function overloading
  2. does python allow multiple inheritance
  3. does python allow multithreading
  4. does python allow method overloading
  5. does python allow recursion
  6. does python allow pointers
  7. what programming paradigm does python allow