简体   繁体   中英

Do forEach async requests inside of a promise?

Need help related to promises. Please refer to details below, topic is more theoretical, I don't understand what flow should I use:

  1. We have async function getDataFromUri(), which returns data, which gets filtered and saved to arr of objects, lets name it res;
  2. For each campaign(object) inside of array res I want send async request which will store products images of campaign to object. As result I should have ONE array res where ALL filtered data (campaign name, campaign images) is stored;

I need smth like:

[{
  name: "Somename",
  images: ['uri', 'uri', 'uri', 'uri', 'uri', 'uri']
},{
  name: "Somename",
  images: ['uri', 'uri', 'uri', 'uri', 'uri', 'uri']
}
]
  1. Do some other actions with RES array.

This function gets campaigns:

function getDataFromUri(uri) {
  return new Promise((resolve, reject) => {
    request.get(uri, (err, res, body) => {
     if(err || res.statusCode !== 200 ) {
       reject(handleErr(err));
     } else {
       resolve(body);
     }
   });
 });
}

This function gets images of campaign:

function getProductsOfCampaign(id) {
  var productsImagesLinks = [];
  return new Promise((resolve, reject) => {
    getDataFromUri(`SOME_URI/${id}.json`)
      .then((json) => {
        var productsList = JSON.parse(json).products;
        resolve (productsList.map((product) => product.imgSrc));
      }).catch((e) => {
        throw new Error(e);
      })
  });
}

Here I met problem:

getDataFromUri(someLink) //Get campaings;
  .then((result) => {

    //NOT WORKING FOREACH

    result.forEach((item, i) => {
      item.images = getProductsOfCampaign(item.id);
    })

    return result;
    })
  .then((result) => {
    //Do something else to array with images;
  });
  1. How can I force next after forEach .then() expression to wait all images URLs to be saved?
  2. I tried Promise.all(), but seems have lack of knowledge on how to implement it correct way.

I will really appreciate if you help me resolve this case. Thank you.

Observe that:

  1. item in forEach is a copy.
  2. getProductsOfCampaign returns a Promise .
  3. The web is a best-effort service.

Do this:

getDataFromUri(someLink) // Get campaigns
  .then(result => {

    var promises = result.map(item =>
      getProductsOfCampaign(item.id)
        .then(products => {
          item.images = products;
          return item;
        })
        // 3: Best-effort service
        .catch(() => {})
    );

    return Promise.all(promises);
  }).then(items => {

    console.log(items);
    // Do something else to array of items with images
  });

Other readers can test for correctness with this:

function getDataFromUri(someLink) {
  return new Promise((resolve) => {
    setTimeout(resolve, 1000, [{id: 1}, {id: 2}]);
  })
}

function getProductsOfCampaign(id) {
  return new Promise((resolve) => {
    setTimeout(resolve, 1000, id * id);
  })
}

var someLink = '';

Thanks to Benjamin Gruenbaum for suggesting that .catch(() => {}) can be used with Promise.all for a best-effort service.

let campaigns = null;
getDataFromUri(someLink) //Get campaings;
 .then((result) => {
  campaigns = result;

  let pImages = []
  result.forEach((item, i) => {
    pImages.push(getProductsOfCampaign(item.id));
  });

  return Promise.all(pImages);
 })
 .then((images) => {
   campaigns.forEach((campaign, index) => {
     campaign.images = images[index];
   });

   // ... Do something else to array with images;
 });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM