0

I have an array of customers and I want to loop through them and assign each one a property. The customer object with the added property is returned via the details function

for (let i = 0; i < customers.length; i++)
{
    customers[i] = await details(customers[i]);
}

This is how im doing it at the moment but its synchronous. I want all objects in the list to be done at once asyncronously. I am trying with the async library but it only gives me the customer object thats available inside of the loop, so the customers array will never change.

async.forEachOf(customers, (customer, finished) async =>
{
    customer = await details(customer);
    finished();
});

How else would I do this? Thanks

Code
  • 39
  • 7
  • I don't think it's very clear what you're asking for. The first code example is the sort of thing I think you want to do, it's not really clear with your descriptions why it's wrong. – TKoL Oct 31 '19 at 17:35
  • If you take the first example, and change it to `customers[i].details = await details(customers[i]);`, is that closer to what you want? – TKoL Oct 31 '19 at 17:36
  • yes its doing exactly what I need but its synchronous. Lets say there is 10 customers, I want all 10 to be done at the same time. The details function returns the customer with the added properties so I want to update that customer by using customers[i] whereas in the 2nd example, customer is defined within the function, so after I cannot return the list of customers with their added functions – Code Oct 31 '19 at 17:40
  • `s/synchronous/sequential`. You're using "synchronous" wrong. – Patrick Roberts Oct 31 '19 at 17:54
  • OH, you want them to be done at the same time. That's easy, I'll make a comment – TKoL Oct 31 '19 at 17:58

3 Answers3

0

You don't need a third-party library to accomplish this. Just use Promise.all() and Array.prototype.map().

const customerDetails = await Promise.all(
  customers.map(customer => details(customer))
);
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
0

A library is not required to iterate over an array of items asynchronously.

For example, to process this array asynchronously:

const arr = [1, 2, 3];

First, create a generator from an array so that can you yield execution for each item in the array:

/**
 * A convenience function for creating a ES2015 Generator to iterate over
 * items in an array.
 *
 * @template T
 * @param {T[]} value
 */
function* toIterator<T>(value: T[]) {
  const generator = value[Symbol.iterator]();
  for (let generated of generator) {
    yield generated;
  }
}

Then, the key to iterating over the array asynchronously is to have a function call itself on the resolve of the provided handler.


/**
 * Provides the ability to process an iterator's values asynchronously
 * with a predicate, `hander`.
 *
 * @template T
 * @template TReturn
 * @template TNext
 * @param {Generator<T, TReturn, TNext>} generator
 * @param {(arg: T) => Promise<TReturn>} handler
 * @returns
 */
async function iterateAsynchronously<T, TReturn, TNext>(
  generator: Generator<T, TReturn, TNext>,
  handler: (arg: T) => Promise<TReturn>
) {
  const iterate: () => Promise<TReturn> = async () => {
    const generated = generator.next();
    if (generated.done) {
      return generated.value;
    }
    return handler(generated.value).then(iterate);
  };
  return iterate();
}

If you prefer, you can extend the global Array with one or both of these methods. Another example.

This would allow you to chain more array methods together after processing the items asynchronously:

const run = async () => {
  const arr = [1, 2, 3, 4];
  console.log((await arr.iterateAsynchronously((val) => new Promise(r => {
    setTimeout(r, 2000);
    console.log('Processing', val);
  }))).reverse());
}
run();

In this case, since the array prototype is modified with the function extension that returns the array itself, you can chain array methods together like you are used to.

Roustalski
  • 155
  • 1
  • 6
-1

This code will run details() for each customer one after the other without waiting for the previous one. You create an array of promises and await them all at the end

const promises = [];
for (let i = 0; i < customers.length; i++) {
    let p = new Promise(async (resolve, reject) => {
      customers[i] = await details(customers[i]);
      resolve();
    });
    promises.push(p);
}
await Promise.all(promises);
TKoL
  • 13,158
  • 3
  • 39
  • 73
  • 1
    It's still wrong. This is why promise construction is an [antipattern](https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it). You haven't properly handled errors. It would be correct if you just `let p = details(customers[i])`, albeit still much more verbose than necessary. – Patrick Roberts Oct 31 '19 at 18:05