6

I have the following code that should add items for customers if they dont already exist. Execution should be in parallel.

await Promise.all(
    customers.map(async (customer) => {
        return customer.items.map(async (item) => {
            return new Promise(async (resolve) => {
                const productExists = someArray.some(
                    (arrayValue) => arrayValue === item.id
                );
                if (!productExists) {
                    logger.info(
                    `customer item ${item.id} does not exist, creating...`
                    );
                    await createCustomerItem(item.id);
                    logger.info(`customer item ${item.id} created.`);

                    someArray.push(item.id);
                } else {
                    logger.info(`customer item ${item.id} already exists, skipping...`);
                }
                resolve(true);
            });
        });
    })

);

logger.info(`All items should now be present`);

The problem is that the execution is not waiting for createCustomerItem to resolve in the cases of !productExists)

This is the log

customer item 32310 does not exist, creating...
customer item ao does not exist, creating...
customer item ute does not exist, creating...
All items should not be present
customer item ao created.
customer item ute created.
customer item 32310 created.

Naturally All items should not be present should come last.

When all the items already exist, then the process looks good.

AngularDebutant
  • 1,436
  • 5
  • 19
  • 41
  • `map` knows nothing about promises,. So unfortunately you can't use map this way. – Keith Dec 06 '20 at 11:22
  • I'd say it is because Promise.all expects an array of Promises, and you are passing it an array of arrays of Promises. Try flatMap instead. – JulienD Dec 06 '20 at 11:28
  • 1
    I think what @JulienD has mentioned in their comment will resolve your issue. – Link Dec 06 '20 at 11:31
  • 2
    ... or simply replace `return customer.items.map()` with `return Promise.all(customer.items.map())` – blex Dec 06 '20 at 11:34
  • [Never pass an `async function` as the executor to `new Promise`](https://stackoverflow.com/q/43036229/1048572)! – Bergi Dec 06 '20 at 13:31

2 Answers2

7

you can do something like this

const fruitsToGet = ['apple', 'grape', 'pear']

const mapLoop = async () => {
  console.log('Start')

  const promises = await fruitsToGet.map(async fruit => {
    const numFruit = new Promise((resolve, reject) => {
      setTimeout(() => resolve(fruit), 1000)
    });
    return numFruit
  })
  const numFruits = await Promise.all(promises)
  console.log(numFruits)

  console.log('End')
}

mapLoop();

results

Start
["apple", "grape", "pear"]
End

source demo

1

Try with flatMap:

await Promise.all(
    customers.flatMap(async (customer) => {
        return customer.items.map(async (item) => {

Instead of returning an array of array of Promises, it will flatten the content to a simple array of Promises, which is what Promise.all expects.

N.B. It is not apparent from your question if the grouping of items per customer needs to be conserved. If it does, note that this solution changes the data structure to a flattened list, so you lose the grouping. Either add some customerId in your items, or try @blex's suggestion in comments.

JulienD
  • 7,102
  • 9
  • 50
  • 84