How can i implement multiple promises inside a loo

2019-08-21 02:00发布

问题:

im trying to filter images by title or description using flicker getinfo request, which returns this information.

what I'm trying to do is to send a getinfo request for each image inside currently images array, see if the title or description match the user input and if so to render this new filtered images array.

I cannot resolve how should I make a call for each image, and only after the loop is over and the filtered array is done, then call setState on that array.


  filterImages = (filter) =>{
    if(filter.length === 0)
      return;

    const currentImages = this.state.images;
    console.log(currentImages[0]);
    const newFilterdImages =[];
    const baseUrl = 'https://api.flickr.com/';

    for (const img of currentImages){
      axios({
      url: `services/rest/?method=flickr.photos.getinfo&api_key=22c1f9009ca3609bcbaf08545f067ad&photo_id=${img.id}&&format=json&safe_search=1&nojsoncallback=1`,
      baseURL: baseUrl,
      method: 'GET'
    })
    .then(res=> res.data)
    .then(res=> {
      if( res && res.photo) {
        const imageInfo = res.photo;
        //console.log(imageInfo.title._content,imageInfo.description._content);

        if(imageInfo.title._content.includes(filter) || imageInfo.description._content.includes(filter)){
            newFilterdImages.push(img);
        }
      }
    }).then( res => console.log("first then " + newFilterdImages)) // output the newFilterdImages array on each call
    }

    this.setState({images:newFilterdImages}) // this will not work 
  }

how can i wait for this loop to end, and only then change the current array with new Filter Images?

回答1:

In Javascript, a Promise represents some future result of a possibly slow operation. Realizing the result of a Promise involves running .then() on it.

To make it easier to wait for a bunch of future results, the method Promise.all() is provided for you. It takes a list of Promises and returns a Promise that resolves with the future values of all of the provided Promises.

Combining these points, we can run axios on all of the images by mapping the list of images to a list of Promises:

let axioses = currentImages.map((img) => {
  return axios({
      url: `services/rest/?method=flickr.photos.getinfo&api_key=22c1f9009ca3609bcbaf08545f067ad&photo_id=${img.id}&&format=json&safe_search=1&nojsoncallback=1`,
      baseURL: baseUrl,
      method: 'GET'
  })
})

...and then running Promise.all() on the resulting list and treating it as a promise that resolves with a list of results from axios() when they're all available:

Promise.all(axioses).then((results) => {
      const filteredResults = results.filter((res) => {
         if (!res || !res.photo) {
            return false
         }

         const imageInfo = res.photo;

         return imageInfo.title._content.includes(filter) || imageInfo.description._content.includes(filter))
     )

     this.setState({ images: filteredResults.map(res => res.photo) })
})


回答2:

You need to make use of Promise.all() to wait for all the images to resolve before filtering them and assigning to state

filterImages = (filter) =>{
    if(filter.length === 0)
      return;

    const currentImages = this.state.images;
    console.log(currentImages[0]);
    const newFilterdImages =[];
    const baseUrl = 'https://api.flickr.com/';
    const promises = [];
    for (const img of currentImages){
      promises.push(axios({
      url: `services/rest/?method=flickr.photos.getinfo&api_key=22c1f9009ca3609bcbaf08545f067ad&photo_id=${img.id}&&format=json&safe_search=1&nojsoncallback=1`,
      baseURL: baseUrl,
      method: 'GET'
    })
    .then(res=> res.data)
    }
    Promise.all(promises).then((data) => {
        data.forEach((item, index) => {
             if( item && item.photo) {
                 const imageInfo = res.photo;
                 if(imageInfo.title._content.includes(filter) || imageInfo.description._content.includes(filter)){
                       newFilterdImages.push(currentImages[index]);
                 }
             }
        });
        this.setState({images:newFilterdImages})
    })
  }