tc39 / proposal-array-from-async

Draft specification for a proposed Array.fromAsync method in JavaScript.
http://tc39.es/proposal-array-from-async/
BSD 3-Clause "New" or "Revised" License
179 stars 13 forks source link

Is each `mapFn` executed concurrently or sequentially? #42

Closed secondl1ght closed 11 months ago

secondl1ght commented 1 year ago

I am interested in this proposal but would like clarification on the above. My use-case would be to filter a current Array using an async mapFn to return a new Array with only the values that passed the filter function.

I would want this to happen sequentially because the mapFn would call an API. I attempted to do this with a regular Array.filter() method but discovered that you cannot use this method using async functions because they will always be truthy.

Please let me know if this new proposal will work for my intended use-case, if so I would like to try out your polyfill package. Thanks!

Josh-Cena commented 1 year ago

Yes, mapFn is sequential. This method desugars to:

for await (const v of array) {
  newArray.push(await mapFn(v));
}

You can easily achieve parallel filtering.

const filteredArray = Promise.all(array.map(async (v) => [v, await callAPI(v)])).then((a) => a.filter(([, c]) => c).map(([v]) => v));
bakkot commented 1 year ago

What @Josh-Cena says is accurate, but for your specific application, the stage 2 async iterator helpers proposal is likely more appropriate - it will let you write

let filtered = await array.values().toAsync().filter(someAsyncPredicate).toArray();

This will be sequential, though you will be able to specify a degree of concurrency with something like

let filtered = await array.values().toAsync().filter(someAsyncPredicate).bufferAhead(5).toArray();
secondl1ght commented 1 year ago

Thanks for the responses, it sounds like I can most easily achieve my goals by using the for await... that @Josh-Cena suggested and use fully supported and stable JS instead of a polyfill that is under heavy development. I noticed in the docs it is NOT recommended to be used in production yet, and what I am building is a production app. So for that reason I will go with this option. I think it will actually work great for my use case!

secondl1ght commented 1 year ago

Here is a code snippet of what I implemented if anyone is interested:

    for await (const invoice of invoices) {
        const validated = await validateInvoice(invoice);

        if (validated) {
            invoicesFiltered.push(invoice);
        }
    }