It should really be in the form of a  static method like `Promise.map` or
some other naming

On Sat, Sep 7, 2019 at 6:40 PM Tom Boutell <[email protected]> wrote:

> Up to five instances of the loop body would be in progress, assuming at
> least one of them awaits at some point. any await inside the loop body
> would be respected with regard to code that follows it in the loop body.
> The only place concurrency comes into play is that more than one of these
> loopbodies could be in progress for separate items, for instance they could
> all be awaiting  a long database operation or API call. To avoid the
> frequent problems that come up with unlimited concurrency when dealing with
> apis and similar, you set the concurrency so that you don't exceed those
> reasonable limits.
>
> On Sat, Sep 7, 2019, 12:24 PM Bob Myers <[email protected]> wrote:
>
>> I don't understand how this would work.
>>
>> ```
>> for (const thing of things concurrency 5) {
>>   const result = await thing();
>>   console.log(result); // <== what is `result` here, if the call to
>> thing() hasn't completed?
>> }
>> ```
>>
>> Also, it's intellectually unsatisfying that I can't specify concurrency
>> for all the async calls in
>>
>> ```
>> await foo();
>> await bar();
>> for (const thing of things) await thing();
>> await baz();
>> ```
>>
>> Bob
>>
>>
>>
>> On Sat, Sep 7, 2019 at 8:17 AM Tom Boutell <[email protected]> wrote:
>>
>>> *REVISED PROPOSAL (thanks for the input so far!)*
>>>
>>> *Background*
>>>
>>> Developers learning async programming in the modern async/await era
>>> frequently discover this useful pattern:
>>>
>>> ```js
>>> for (item of items) {
>>>   await db.insert(item);
>>>   // additional awaited operations, etc.
>>> }
>>> ```
>>>
>>> This pattern extends the readability of "await" to cases where each item
>>> in an array, or each item in an iterable, requires some asynchronous
>>> processing. It also ensures that items are processed one at a time,
>>> avoiding unbounded concurrency that results in excessive stress on back
>>> ends, triggers API rate limiting, and otherwise results in unpredictable
>>> bugs.
>>>
>>> However, a common next step is to wish for a manageable level of
>>> concurrency. For instance, processing 500 asynchronous calls at once is
>>> unwise, especially in a web application that is already handling 100
>>> requests at once. But, processing 5 at once may be reasonable and improve
>>> the processing time per request.
>>>
>>> Unfortunately, at present, this requires shifting mental gears from
>>> async/await to promises. Here is an example based on Bluebird, there are of
>>> course other libraries for this:
>>>
>>> ```js
>>> const Promise = require('bluebird');
>>> await Promise.map(items, async function(item) {
>>>   await db.insert(item);
>>>   // additional awaited operations, etc.
>>> }, *{ concurrency: 5 }*);
>>> ```
>>>
>>> While effective, this is a lot of boilerplate and a shift of mental
>>> model. And in my experience as a mentor to many developers, this is *the
>>> only situation in which they frequently need to reach for a promise
>>> library. *A language feature to naturally integrate it with async/await
>>> would substantially expand the effectiveness of async/await as a tool for
>>> reducing the cognitive load of asynchronous programming.
>>>
>>> *Proposed Feature*
>>>
>>> I propose extending the existing async / await syntax to accommodate 
>>> *specifying
>>> the concurrency of a for...of loop:*
>>>
>>> ```js
>>> for (item of items *concurrency 5*) {
>>>   // db.insert is an async function
>>>   await db.insert(item);
>>> }
>>> console.log('Processing Complete');
>>> ```
>>>
>>> The major benefit here is that *the developer does not have to shift
>>> gears mentally from async/await to thinking about promises to deal with a
>>> common case in systems programming. *They are able to continue with the
>>> pattern they are already comfortable with.
>>>
>>> Up to 5 loop bodies commence concurrently with regard to "await"
>>> statements (see below).
>>>
>>> There is no guarantee that item 3 will finish before item 2, or that
>>> item 4 won't start (due to 3 being finished) before item 2 ends, etc.
>>>
>>> If an exception is not caught inside the loop body, the loop stops, that
>>> exception is thrown beyond the loop, and any further exceptions from that
>>> invocation of the loop due to concurrently executing loop bodies are
>>> discarded.
>>>
>>> Just as with an ordinary "for...of" loop containing an "await"
>>> statement, *it is guaranteed that, barring an exception, the loop body
>>> will execute completely for every item in "items" before the loop exits and
>>> the console.log statement executes.* The only difference is that *the
>>> specified amount of concurrency is permitted during the loop*.
>>>
>>> "5" may be any expression resulting in a number. It is cast to an
>>> integer. If  the result is not a natural number, an error is thrown (it
>>> must not be 0 or a negative number).
>>>
>>> *FAQs*
>>>
>>> *"What if I want unlimited concurrency?"*
>>>
>>> It is rarely a good idea. It results in excessive stress on back ends,
>>> unnecessary guards that force serialization in interface libraries just to
>>> cope with (and effectively negate) it, and API rate limiting. This feature
>>> teaches the best practice that the level of concurrency should be mindfully
>>> chosen. However, those who really want it can specify "concurrency
>>> items.length" or similar.
>>>
>>> *"What about async iterators?"*
>>>
>>> The feature should also be supported here:
>>>
>>> ```js
>>> for *async* (item of items *concurrency 5*) {
>>>   // db.insert is an async function
>>>   await db.insert(item);
>>> }
>>> ```
>>>
>>> While the async iterator itself is still sequential rather than
>>> concurrent, frequently these can supply values considerably faster than
>>> they are processed by the loop body, and so there is still potential
>>> benefit in having several items "in the hopper" (up to the concurrency
>>> limit) at a time.
>>>
>>> --
>>> Chief Software Architect
>>> Apostrophe Technologies
>>> Pronouns: he / him / his
>>> _______________________________________________
>>> es-discuss mailing list
>>> [email protected]
>>> https://mail.mozilla.org/listinfo/es-discuss
>>>
>> _______________________________________________
> es-discuss mailing list
> [email protected]
> https://mail.mozilla.org/listinfo/es-discuss
>
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to