SecondClassCitizen
SecondClassCitizen

Reputation: 111

Detect end of forEach loop that contains http requests?

I have this function and inside forEach loop that iterate trough items... What I want to achieve is to to render some spinner ( loader ) until all of http requests are done, until forEach loop is done.

The way I am trying now, first and last console.log triggers immediately and then it logs second console.log as many times as there are items in the items array.

How can I update state to loading: false when forEach iteration is done, when all http requests are done?

someHandler = () => {
    // this.setState({ loading: false })
       console.log( "Not loading" )
    this.props.items.forEach(item => {
      axios
        .get(`.../${item}/`)
        .then(res => {
          this.setState(prevState => ({
            item: [
              ...prevState.item,
              {
                name: res.data.name,
                img: res.data.img
              }
            ]
          }));
          this.props.newItem(this.state.item);
          // this.setState({ loading: true })
             console.log( "Loading" )
        })
        .catch(err => {
          console.log(err);
          // this.setState({ loading: false })
             console.log( "Not loading" )
        });
    });
    // this.setState({ loading: false })
       console.log( "Not loading" )
  };

Upvotes: 1

Views: 638

Answers (2)

idlefingers
idlefingers

Reputation: 32037

If you can't use async/await in your env, and want to wait until a number of promises are resolved, then Promise.all is your friend. I may have missed something, but in a nutshell, you could rewrite your function to be like this:

const requests = this.props.items.map(item => (
  axios.get(`...${item}`).then(res => {
    const newItem = { name: res.data.name, img: res.data.img }
    this.props.newItem(newItem)
    return newItem
  })
))

Promise.all(requests).then(responses => {
  this.setState({ item: responses })
})

This creates a new array of promises called requests and when they're all done, uses setState to set all the responses.

Upvotes: 1

Dinesh Pandiyan
Dinesh Pandiyan

Reputation: 6289

You can use async/await with for...of

You can assume sequential execution with this approach.

someHandler = async () => {
  console.log('Not loading'); // will execute before loop
  this.setState({ loading: true }); // start loading

  for (let item of this.props.items) {
    const res = await axios.get(`.../${item}/`);
    // ... do your logic with response
    console.log('Loading');
  }

  console.log('Not loading'); // will execute after loop
  this.setState({ loading: false }); // end loading
};

Upvotes: 1

Related Questions