user1840267
user1840267

Reputation: 428

Promises.all with additional values

New to promises, would like to learn. I have this array of shopIds:

let shopIdsArray = ['shop1','shop2','shop3'];

and an external promise call

getProducts(shopId, 'products') //promise returns the products of this shop

Unfortunately, the promise does not return the shopId, just the products, so I have to somehow keep the shopId and store it together with the products once the promise is finished. My current approach is to call the promise with each shopId, and add the result of each promise call to a shopsAndProductsArray like this:

shopsAndProductsArray.push({
    "id":shopId,
    "products":products
}); 

Later on, my "wrapper promise" should return the finished shopsAndProductsArray.

My current code (node, ES6) looks like:

updateProducts = new Promise(

    function (resolve, reject) {
        let shopIdsArray = ['shop1','shop2','shop3'];
        const shopsAndProductsArray = [];
        let promisesArray = [];

        shopIdsArray.forEach((shopId) => {
            let promise = getProducts(shopId, 'products')
                    .then((products) => {
                        const shopInfoObject = {
                                 "id":shopId,
                                 "products":products
                        };
                        console.log("sio: ",shopInfoObject); //prints shopIds and products fine.
                        shopsAndProductsArray.push(shopInfoObject);
                        promisesArray.push(promise);
                    });

        });

        Promise.all(promisesArray)
            .then(function (shopsAndProductsArray) {
                resolve(shopsAndProductsArray); //the shopsAndProductsArray is undefined?
            });
    }
);

On resolve, the shopsAndProductsArray is...not empty, but consists of the same number of entries as the shopIdsArray, but the array items are undefined. I probably should put the promises into an array first, then work on them in Promise.all, but at that point I lost the reference to which shop id the promise belongs.

I read many other examples about iteration and promises and tried many other ways but it seems that I haven't fully understood what is called at which point. I'm sure filling the promises array the way I do is wrong, but I had no better idea how to invoke Promise.all.

I think I could narrow my questions down to:

  1. How should I iterate over the shopIds, calling the promise with each?
  2. How can I preserve the shopId once a promise returns a list of products?
  3. How can I return the array of shopIds and products once all shopIds have been processed?

Thanks in advance for any help.

EDIT: Thank you so much, justelouise and Felix Kling. I knew I was overcomplicating things, but couldn't put my finger where. I understand now what I was missing, thanks to your examples. I think your answers are essentially equal and both thoroughly explained. I give the accept checkmark to justelouise because she appears to be first and Felix Kling has slightly more reputation O_o.

Upvotes: 5

Views: 4049

Answers (2)

Felix Kling
Felix Kling

Reputation: 816404

Your code can be simplified a lot to:

updateProducts = Promise.all(
  ['shop1','shop2','shop3'].map(
    id => getProducts(id, 'products').then(products => ({id, products}))
  )
);

It's exactly the same approach as yours, just less verbose.

Since Promise.all already returns a promise, there is no need to put a new Promise(...) around it.

If you want to convert every element of an array to something else, then Array#map is a more useful method. It applies the passed callback to every element of the array and returns an array of its return values. In your case you want to create a Promise for each shop ID, and that's what

['shop1','shop2','shop3'].map(id => getProducts(id, 'products'))

does.

Now, since you do not just want to get the products but also the ID, we have to modify the result of getProducts a little bit, which is what the

.then(products => ({id, products}))

does. There is nothing really special about this. We simply return an object with the two properties id and products instead of just the products array.

With Promise.all and Array#map you don't need to "manually" keep track of the promises (promisesArray) and the results (shopsAndProductsArray).

Upvotes: 1

justelouise
justelouise

Reputation: 770

I think you can simplify your functions as such:

return Promise.all(shopIdsArray.map(shopId => {
  return getProducts(shopId).then((products) => {
    return {
      id: shopId,
      products,
    };
  });
}));

Promise.all returns an array of promises created as you are iterating thru the array via the map function where in getProducts call is executed for each shop id. Once the products data is returned, you still have access with the corresponding shop id for it and return a new object consisting of the id and the results

return {
  id: shopId,
  products,
};

Lastly, Promise.all returns an array containing the returned value for each promise executed within it.

Upvotes: 11

Related Questions