Proposal: `await.all {...}` for parallelism

manuelbarzi manuelbarzi at gmail.com
Mon Nov 25 14:19:01 UTC 2019


why not making it work with the addition of a new keyword suffix for
parallel awaits (for example, `||`), grouping the mid blocks that require
parallelism. playing with your example and going a bit further:

```
async {
  const v0 = await|| returnsAPromise(); // to be grouped in parallel
  for (let i = 0; (i < 100000); i++) {
    doesSimpleFastSynchronousMath();
  }
  const v1 = await|| returnsAnotherPromise(); // to be grouped in parallel
  async {
      await returnsAnotherPromise1();
      const v2 = await|| returnsAnotherPromise2(); // to be grouped in
parallel
      const v3 = await|| returnsAnotherPromise3(); // to be grouped in
parallel
      await returnsAnotherPromise4(v2, v3);
      const v4 = await returnsAnotherPromise5();
  }
  await returnsAnotherPromiseX(v0, v1);
}
```

On Mon, Nov 25, 2019 at 2:06 PM Tom Boutell <tom at apostrophecms.com> wrote:

> There is however a performance concern with your code that we should talk
> about.
>
> If I write this:
>
> await.all {
>   returnsAPromise();
>   for (let i = 0; (i < 100000); i++) {
>     doesSimpleFastSynchronousMath();
>   }
>   returnsAnotherPromise();
> }
>
> Then Babel will have no choice but to compile this to:
>
> {
>   const promises = [];
>   {
>     const maybeThenable = returnsAPromise();
>     if (maybeThenable && maybeThenable.then) {
>       promises.push(maybeThenable);
>     }
>   }
>   promises.push(returnsAPromise());
>   for (let i = 0; (i < 100000); i++) {
>     const maybeThenable = doesSimpleFastSynchronousMath();
>     if (maybeThenable && maybeThenable.then) {
>       promises.push(maybeThenable);
>     }
>   }
>   const maybeThenable = returnsAnotherPromise();
>   if (maybeThenable && maybeThenable.then) {
>     promises.push(maybeThenable);
>   }
> }
> await Promise.all(promises);
>
> Which could have a significant performance impact on that synchronous
> inner loop.
>
>
>
> On Mon, Nov 25, 2019 at 7:55 AM Tom Boutell <tom at apostrophecms.com> wrote:
>
>> Hey, you're absolutely right! It's OK because it just means things are
>> more deterministic before the block exits. It doesn't impact any reasonable
>> expectations *during* the block.
>>
>> I am convinced that your syntax is useful and does not introduce any new
>> confusion.
>>
>> I wonder, then, if it is also possible to implement concurrency limits
>> for this properly?
>>
>> await.all({ concurrency: 5 }) {
>>   for (const item of items) {
>>     // returns promise
>>     item.process();
>>   }
>> }
>>
>> This is more challenging because, in our transpilation, we can't just
>> bottle up all the promises and call Promise.all at the end. It would be too
>> late to manage how many are in process at once, bashing on various API
>> limits (:
>>
>> On Sun, Nov 24, 2019 at 7:43 PM Naveen Chawla <naveen.chwl at gmail.com>
>> wrote:
>>
>>> Hi! It does not change the meaning of the ";" at all. As you may already
>>> know, omitting `await` already invokes multiple async function calls in
>>> parallel, in current JavaScript, so absolutely no change in that respect.
>>>
>>> The only thing this `await.all` suggestion does, is ensure that all
>>> non-awaited async function calls are completed before proceeding beyond the
>>> end of the block.
>>>
>>> i.e. it adds fairly straightforward and terse deterministic control to
>>> otherwise non-deterministic code, without requiring knowledge of
>>> destructuring or `Promise.all`.
>>>
>>> On Sat, 23 Nov 2019 at 13:25, Tom Boutell <tom at apostrophecms.com> wrote:
>>>
>>>> This is very interesting, but this code:
>>>>
>>>> await.all {
>>>>    x = getXAsync();
>>>>    y = getYAsync();
>>>> }
>>>>
>>>> processXAndY(x, y);
>>>>
>>>> Still carries within it the problem that if I'm looking at just the
>>>> middle of the { ... } block — if "await.all" has scrolled offscreen — I'll
>>>> be completely wrong about what ";" means. I think that's too much magic.
>>>>
>>>> Also, in the case of the "for" loop, this doesn't address managing the
>>>> level of concurrency. Although it could in theory with a syntax like
>>>> await.all({ concurrency: 5 }), I'm not sure if it's practical to implement
>>>> that for your general case.
>>>>
>>>> Actually I'm curious about what the implementation would look like in
>>>> general.  If it were babel compiling this, I guess it would have to wrap
>>>> every statement not preceded by "await" with a check for whether it returns
>>>> a thenable and add it to an array if it does. But with the concurrency
>>>> feature it would also have to defer executing the code at all until the
>>>> right time as otherwise we're still starting zillions of "processes" at
>>>> once.
>>>>
>>>>
>>>> On Sat, Nov 23, 2019 at 5:08 AM Naveen Chawla <naveen.chwl at gmail.com>
>>>> wrote:
>>>>
>>>>> However, if `await.all { ... }` were to mean "wait for all non-awaited
>>>>> async function calls made within this block to complete before proceeding",
>>>>> as I suggested earlier, I think that could satisfy determinism for "await"
>>>>> wherever it is used, and satisfy the original motivation:
>>>>>
>>>>> ```
>>>>> await.all {
>>>>>     for (const item of items) {
>>>>>         doTheThingAsync(item);
>>>>>     }
>>>>> }
>>>>> ```
>>>>>
>>>>> Notice I have omitted `await` inside the loop. Like current
>>>>> JavaScript, that causes parallel execution, so no change on that front,
>>>>> from a determinism perspective. So determinism is not hurt by `await.all`.
>>>>> Rather, it guarantees completion before going further.
>>>>>
>>>>> In an earlier example (paraphrase-coded as I forgot the names):
>>>>>
>>>>> ```
>>>>> let x, y;
>>>>>
>>>>> await.all {
>>>>>    x = getXAsync();
>>>>>    y = getYAsync();
>>>>> }
>>>>>
>>>>> processXAndY(x, y);
>>>>> ```
>>>>>
>>>>> I think the benefit of this syntax appears more stark with the looped
>>>>> (first) example, as current JavaScript requires building an array in the
>>>>> loop to subsequently pass to `Promise.all`, which I think is a little more
>>>>> difficult to conceptualize than the `await.all { ... }` way of doing it.
>>>>> The 2nd example is arguably better than current JavaScript too,
>>>>> particularly because the coder doesn't have to be very smart with
>>>>> destructuring in light of understanding the "Promise.all" return type, etc.
>>>>> In other words, less cognitive overhead, which I think is a net positive.
>>>>>
>>>>> On Fri, 22 Nov 2019 at 13:44, Tom Boutell <tom at apostrophecms.com>
>>>>> wrote:
>>>>>
>>>>>> I am very sympathetic to pitches to allow more common cases for
>>>>>> promise libraries to be written in an "awaitful" syntax without thinking
>>>>>> explicitly about promises.
>>>>>>
>>>>>> Howeever I think that changing the meaning of the semicolon in a
>>>>>> particular context has too much potential for confusion. As others have
>>>>>> said, parallel execution is different, and it should look and feel
>>>>>> different. The most basic assumption a developer makes (consecutive lines
>>>>>> of code run consecutively) is difficult to get away from; that's why we
>>>>>> introduced "await" in the first place, to bring back the ability to write
>>>>>> deterministic code with consecutive statements. Which sounds like a
>>>>>> reasonable ask, when it's put that way. (:
>>>>>>
>>>>>> I did propose this recently:
>>>>>>
>>>>>> for (const item of items concurrency 5) {
>>>>>>   await  doTheThing(item);
>>>>>> }
>>>>>>
>>>>>> However in this case I'm not talking about consecutive statements,
>>>>>> I'm only talking about rules for simultaneously (in the sense of async, not
>>>>>> threads) running more than one instance of the block. So I'm not proposing
>>>>>> that we change the meaning of the semicolon(s) *within* the block in a way
>>>>>> that could mean that if you're looking at half the code in the middle you
>>>>>> would be likely to fundamentally misunderstand its operation.
>>>>>>
>>>>>> I think that risk - that you can't tell what a semicolon means
>>>>>> without reference to the outer context - is what makes your proposal a
>>>>>> bridge too far for me.
>>>>>>
>>>>>>
>>>>>> --
>>>>>>
>>>>>> THOMAS BOUTELL | CHIEF TECHNOLOGY OFFICER
>>>>>> APOSTROPHECMS | apostrophecms.com | he/him/his
>>>>>> _______________________________________________
>>>>>> es-discuss mailing list
>>>>>> es-discuss at mozilla.org
>>>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>>>
>>>>>
>>>>
>>>> --
>>>>
>>>> THOMAS BOUTELL | CHIEF TECHNOLOGY OFFICER
>>>> APOSTROPHECMS | apostrophecms.com | he/him/his
>>>>
>>>
>>
>> --
>>
>> THOMAS BOUTELL | CHIEF TECHNOLOGY OFFICER
>> APOSTROPHECMS | apostrophecms.com | he/him/his
>>
>
>
> --
>
> THOMAS BOUTELL | CHIEF TECHNOLOGY OFFICER
> APOSTROPHECMS | apostrophecms.com | he/him/his
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20191125/ee65ec8c/attachment-0001.html>


More information about the es-discuss mailing list