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

classic Classic list List threaded Threaded
48 messages Options
123
Reply | Threaded
Open this post in threaded view
|

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

Naveen Chawla
OK I'm even more confused now. x1 is surely not a resolved value until all the next "non parallel await" so is it "undefined" until then?

Could you give an example of what you mean by the `await.all { ... }` block syntax bringing "complexity on returning values assignment, specially when" "about constants (`const`)", as I'm unclear what you are referring to.

Cheers,
N

On Tue, 26 Nov 2019 at 13:35, manuelbarzi <[hidden email]> wrote:
 
Why not just maximally preserve current JavaScript for parallel execution, just by omitting `await` in multiple async calls, simply wrapping it in an `await.all` block to ensure completion before code continues past the block. This surely is the more straightforward way to satisfy the same goals?

because wrapping it an `await.all` on the one hand explicitly groups promises, but brings complexity on returning values assignment, specially when is about constants (`const`). so, if you avoid blocks, just marking parallel awaits with, for example, a suffix `await||`, or whatever other more convenient way, you can just write the code in series as normally, and avoid that complexity. the transpiler would just require to group the consecutive marked parallel awaits (`await||`) into a Promise.all() and that's it. following i reproduce the demo before:

```
// NOTE p? = function call that returns a promise (? = just and index)
// NOTE s? = function call that runs synchronously and returns a value (? = just and index)

const x0 = await p0()

const x11 = s11() // sync code in-the-middle

const x1 = await || p1(x0)
const x3 = await || p3(x11)
const x2 = await p2(x1)
const x10 = await p10(x2x3)

const x12 = s12() // sync code in-the-middle

const x4 = await || p4(x1x2)
const x5 = await || p5(x2x3x12)
const x6 = await p6(x4x5x10)

const x7 = await || p7(x4x6)
const x9 = await || p9(x5x6)
const x8 = await p8(x6x7)

await p11(x8x9)

// it would resolve a tree of parallel and series like following with traditional promises

p0
    .then(x0 => {
        const x11 = f11()

        return Promise.all([p1(x0), p3(x11)])
            .then((x1x3=>
                p2(x1)
                    .then(x2 =>
                        p10(x2x3)
                            .then(x10 => {
                                const x12 = s12()

                                return Promise.all([p4(x1x2), p5(x2x3x12)])
                                    .then((x4x5=>
                                        p6(x4x5x10)
                                            .then(x6 => Promise.all([p7(x4x6), p9(x5x6)])
                                                .then((x7x9=> p8(x6x7)
                                                    .then(x8 => p11(x8x9))
                                                )
                                            )
                                    )
                            })
                    )
            )
    })
```

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

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

manuelbarzi

OK I'm even more confused now. x1 is surely not a resolved value until all the next "non parallel await" so is it "undefined" until then?

as a `const` x1 does not exist until those parallel awaits `await||` (for p1 and p3) are resolved (same for x3). then p2 is resolved after that.

what it tries to bring is a simplification of syntax.

Could you give an example of what you mean by the `await.all { ... }` block syntax bringing "complexity on returning values assignment, specially when" "about constants (`const`)", as I'm unclear what you are referring to

`const` and `let` are block-scoped, so this is referring to avoid that block notation, and then keep coherent with the possibility to directly infer in current scope references without extra segments / blocks.


On Tue, 26 Nov 2019 at 13:35, manuelbarzi <[hidden email]> wrote:
 
Why not just maximally preserve current JavaScript for parallel execution, just by omitting `await` in multiple async calls, simply wrapping it in an `await.all` block to ensure completion before code continues past the block. This surely is the more straightforward way to satisfy the same goals?

because wrapping it an `await.all` on the one hand explicitly groups promises, but brings complexity on returning values assignment, specially when is about constants (`const`). so, if you avoid blocks, just marking parallel awaits with, for example, a suffix `await||`, or whatever other more convenient way, you can just write the code in series as normally, and avoid that complexity. the transpiler would just require to group the consecutive marked parallel awaits (`await||`) into a Promise.all() and that's it. following i reproduce the demo before:

```
// NOTE p? = function call that returns a promise (? = just and index)
// NOTE s? = function call that runs synchronously and returns a value (? = just and index)

const x0 = await p0()

const x11 = s11() // sync code in-the-middle

const x1 = await || p1(x0)
const x3 = await || p3(x11)
const x2 = await p2(x1)
const x10 = await p10(x2x3)

const x12 = s12() // sync code in-the-middle

const x4 = await || p4(x1x2)
const x5 = await || p5(x2x3x12)
const x6 = await p6(x4x5x10)

const x7 = await || p7(x4x6)
const x9 = await || p9(x5x6)
const x8 = await p8(x6x7)

await p11(x8x9)

// it would resolve a tree of parallel and series like following with traditional promises

p0
    .then(x0 => {
        const x11 = f11()

        return Promise.all([p1(x0), p3(x11)])
            .then((x1x3=>
                p2(x1)
                    .then(x2 =>
                        p10(x2x3)
                            .then(x10 => {
                                const x12 = s12()

                                return Promise.all([p4(x1x2), p5(x2x3x12)])
                                    .then((x4x5=>
                                        p6(x4x5x10)
                                            .then(x6 => Promise.all([p7(x4x6), p9(x5x6)])
                                                .then((x7x9=> p8(x6x7)
                                                    .then(x8 => p11(x8x9))
                                                )
                                            )
                                    )
                            })
                    )
            )
    })
```

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

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

manuelbarzi
however, i find yet things to solve with it, like how to face two consecutive and depending parallel blocks (no series awaits neither sync statements in the middle). for that, the initial proposal using arrays would fit, but i understand is what you trying to avoid.

const [x1, x2] = await|| [p1(), p2()]

const [x3, x4] = await|| [p3(x1), p4(x2)]

... do whatever with x3 and x4


On Tue, 26 Nov 2019 at 15:13, manuelbarzi <[hidden email]> wrote:

OK I'm even more confused now. x1 is surely not a resolved value until all the next "non parallel await" so is it "undefined" until then?

as a `const` x1 does not exist until those parallel awaits `await||` (for p1 and p3) are resolved (same for x3). then p2 is resolved after that.

what it tries to bring is a simplification of syntax.

Could you give an example of what you mean by the `await.all { ... }` block syntax bringing "complexity on returning values assignment, specially when" "about constants (`const`)", as I'm unclear what you are referring to

`const` and `let` are block-scoped, so this is referring to avoid that block notation, and then keep coherent with the possibility to directly infer in current scope references without extra segments / blocks.


On Tue, 26 Nov 2019 at 13:35, manuelbarzi <[hidden email]> wrote:
 
Why not just maximally preserve current JavaScript for parallel execution, just by omitting `await` in multiple async calls, simply wrapping it in an `await.all` block to ensure completion before code continues past the block. This surely is the more straightforward way to satisfy the same goals?

because wrapping it an `await.all` on the one hand explicitly groups promises, but brings complexity on returning values assignment, specially when is about constants (`const`). so, if you avoid blocks, just marking parallel awaits with, for example, a suffix `await||`, or whatever other more convenient way, you can just write the code in series as normally, and avoid that complexity. the transpiler would just require to group the consecutive marked parallel awaits (`await||`) into a Promise.all() and that's it. following i reproduce the demo before:

```
// NOTE p? = function call that returns a promise (? = just and index)
// NOTE s? = function call that runs synchronously and returns a value (? = just and index)

const x0 = await p0()

const x11 = s11() // sync code in-the-middle

const x1 = await || p1(x0)
const x3 = await || p3(x11)
const x2 = await p2(x1)
const x10 = await p10(x2x3)

const x12 = s12() // sync code in-the-middle

const x4 = await || p4(x1x2)
const x5 = await || p5(x2x3x12)
const x6 = await p6(x4x5x10)

const x7 = await || p7(x4x6)
const x9 = await || p9(x5x6)
const x8 = await p8(x6x7)

await p11(x8x9)

// it would resolve a tree of parallel and series like following with traditional promises

p0
    .then(x0 => {
        const x11 = f11()

        return Promise.all([p1(x0), p3(x11)])
            .then((x1x3=>
                p2(x1)
                    .then(x2 =>
                        p10(x2x3)
                            .then(x10 => {
                                const x12 = s12()

                                return Promise.all([p4(x1x2), p5(x2x3x12)])
                                    .then((x4x5=>
                                        p6(x4x5x10)
                                            .then(x6 => Promise.all([p7(x4x6), p9(x5x6)])
                                                .then((x7x9=> p8(x6x7)
                                                    .then(x8 => p11(x8x9))
                                                )
                                            )
                                    )
                            })
                    )
            )
    })
```

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

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

Naveen Chawla
In reply to this post by manuelbarzi
If I have, as per your examples,

x1 = await||actionAsync1()
x2 = await||actionAsync2(x1) //x1 is undefined here, only resolved on the next "non-parallel-await"

vs adding a line between the two calls:

x1 = await||actionAsync1()
let c;
x2 = await||actionAsync2(x1)

...does the `let c` automatically break the parallel grouping since it's a non-parallel operation (thereby making x1 defined)?

It seems to me like you are doing block logic without blocks, which I think increases the chances of bugs. Also you're not leveraging the existing parallel execution pattern for async functions (which is just to omit the `await`), so it would seem you would be increasing learning overhead. And, you're not really allowing for submitting more sophisticated mixtures of serial async, parallel async and synchronous code for "parallel completion" guarantee, by requiring that parallel calls be "grouped" together in terms of lines of code, almost allowing for nothing beyond the current "Promise.all" pattern, logically. I don't think this is satisfying the original motivation.

For a `awail.all { ... }` block, maybe allowing a "return"/"yield" value could neaten up the block scope separation, but maybe that could be left to the "do" expression if that were adopted. But I don't think it's a big sacrifice if neither exist.


On Tue, 26 Nov 2019 at 14:14, manuelbarzi <[hidden email]> wrote:

OK I'm even more confused now. x1 is surely not a resolved value until all the next "non parallel await" so is it "undefined" until then?

as a `const` x1 does not exist until those parallel awaits `await||` (for p1 and p3) are resolved (same for x3). then p2 is resolved after that.

what it tries to bring is a simplification of syntax.

Could you give an example of what you mean by the `await.all { ... }` block syntax bringing "complexity on returning values assignment, specially when" "about constants (`const`)", as I'm unclear what you are referring to

`const` and `let` are block-scoped, so this is referring to avoid that block notation, and then keep coherent with the possibility to directly infer in current scope references without extra segments / blocks.


On Tue, 26 Nov 2019 at 13:35, manuelbarzi <[hidden email]> wrote:
 
Why not just maximally preserve current JavaScript for parallel execution, just by omitting `await` in multiple async calls, simply wrapping it in an `await.all` block to ensure completion before code continues past the block. This surely is the more straightforward way to satisfy the same goals?

because wrapping it an `await.all` on the one hand explicitly groups promises, but brings complexity on returning values assignment, specially when is about constants (`const`). so, if you avoid blocks, just marking parallel awaits with, for example, a suffix `await||`, or whatever other more convenient way, you can just write the code in series as normally, and avoid that complexity. the transpiler would just require to group the consecutive marked parallel awaits (`await||`) into a Promise.all() and that's it. following i reproduce the demo before:

```
// NOTE p? = function call that returns a promise (? = just and index)
// NOTE s? = function call that runs synchronously and returns a value (? = just and index)

const x0 = await p0()

const x11 = s11() // sync code in-the-middle

const x1 = await || p1(x0)
const x3 = await || p3(x11)
const x2 = await p2(x1)
const x10 = await p10(x2x3)

const x12 = s12() // sync code in-the-middle

const x4 = await || p4(x1x2)
const x5 = await || p5(x2x3x12)
const x6 = await p6(x4x5x10)

const x7 = await || p7(x4x6)
const x9 = await || p9(x5x6)
const x8 = await p8(x6x7)

await p11(x8x9)

// it would resolve a tree of parallel and series like following with traditional promises

p0
    .then(x0 => {
        const x11 = f11()

        return Promise.all([p1(x0), p3(x11)])
            .then((x1x3=>
                p2(x1)
                    .then(x2 =>
                        p10(x2x3)
                            .then(x10 => {
                                const x12 = s12()

                                return Promise.all([p4(x1x2), p5(x2x3x12)])
                                    .then((x4x5=>
                                        p6(x4x5x10)
                                            .then(x6 => Promise.all([p7(x4x6), p9(x5x6)])
                                                .then((x7x9=> p8(x6x7)
                                                    .then(x8 => p11(x8x9))
                                                )
                                            )
                                    )
                            })
                    )
            )
    })
```

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

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

Isiah Meadows-3
Just wanted to drop in and remind people of this by me earlier in the thread: 

The way things are shaping up, it's starting to look like an ad-hoc version of this proposal of mine: 

As I stated earlier, I feel it's premature, especially before we figure out how observables fit into it all.

On Tue, Nov 26, 2019 at 09:48 Naveen Chawla <[hidden email]> wrote:
If I have, as per your examples,

x1 = await||actionAsync1()
x2 = await||actionAsync2(x1) //x1 is undefined here, only resolved on the next "non-parallel-await"

vs adding a line between the two calls:

x1 = await||actionAsync1()
let c;
x2 = await||actionAsync2(x1)

...does the `let c` automatically break the parallel grouping since it's a non-parallel operation (thereby making x1 defined)?

It seems to me like you are doing block logic without blocks, which I think increases the chances of bugs. Also you're not leveraging the existing parallel execution pattern for async functions (which is just to omit the `await`), so it would seem you would be increasing learning overhead. And, you're not really allowing for submitting more sophisticated mixtures of serial async, parallel async and synchronous code for "parallel completion" guarantee, by requiring that parallel calls be "grouped" together in terms of lines of code, almost allowing for nothing beyond the current "Promise.all" pattern, logically. I don't think this is satisfying the original motivation.

For a `awail.all { ... }` block, maybe allowing a "return"/"yield" value could neaten up the block scope separation, but maybe that could be left to the "do" expression if that were adopted. But I don't think it's a big sacrifice if neither exist.


On Tue, 26 Nov 2019 at 14:14, manuelbarzi <[hidden email]> wrote:

OK I'm even more confused now. x1 is surely not a resolved value until all the next "non parallel await" so is it "undefined" until then?

as a `const` x1 does not exist until those parallel awaits `await||` (for p1 and p3) are resolved (same for x3). then p2 is resolved after that.

what it tries to bring is a simplification of syntax.

Could you give an example of what you mean by the `await.all { ... }` block syntax bringing "complexity on returning values assignment, specially when" "about constants (`const`)", as I'm unclear what you are referring to

`const` and `let` are block-scoped, so this is referring to avoid that block notation, and then keep coherent with the possibility to directly infer in current scope references without extra segments / blocks.


On Tue, 26 Nov 2019 at 13:35, manuelbarzi <[hidden email]> wrote:
 
Why not just maximally preserve current JavaScript for parallel execution, just by omitting `await` in multiple async calls, simply wrapping it in an `await.all` block to ensure completion before code continues past the block. This surely is the more straightforward way to satisfy the same goals?

because wrapping it an `await.all` on the one hand explicitly groups promises, but brings complexity on returning values assignment, specially when is about constants (`const`). so, if you avoid blocks, just marking parallel awaits with, for example, a suffix `await||`, or whatever other more convenient way, you can just write the code in series as normally, and avoid that complexity. the transpiler would just require to group the consecutive marked parallel awaits (`await||`) into a Promise.all() and that's it. following i reproduce the demo before:

```
// NOTE p? = function call that returns a promise (? = just and index)
// NOTE s? = function call that runs synchronously and returns a value (? = just and index)

const x0 = await p0()

const x11 = s11() // sync code in-the-middle

const x1 = await || p1(x0)
const x3 = await || p3(x11)
const x2 = await p2(x1)
const x10 = await p10(x2x3)

const x12 = s12() // sync code in-the-middle

const x4 = await || p4(x1x2)
const x5 = await || p5(x2x3x12)
const x6 = await p6(x4x5x10)

const x7 = await || p7(x4x6)
const x9 = await || p9(x5x6)
const x8 = await p8(x6x7)

await p11(x8x9)

// it would resolve a tree of parallel and series like following with traditional promises

p0
    .then(x0 => {
        const x11 = f11()

        return Promise.all([p1(x0), p3(x11)])
            .then((x1x3=>
                p2(x1)
                    .then(x2 =>
                        p10(x2x3)
                            .then(x10 => {
                                const x12 = s12()

                                return Promise.all([p4(x1x2), p5(x2x3x12)])
                                    .then((x4x5=>
                                        p6(x4x5x10)
                                            .then(x6 => Promise.all([p7(x4x6), p9(x5x6)])
                                                .then((x7x9=> p8(x6x7)
                                                    .then(x8 => p11(x8x9))
                                                )
                                            )
                                    )
                            })
                    )
            )
    })
```
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
--

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

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

Naveen Chawla
I don't know that an `await.all { ... }` block would be premature, especially since it's straightforward, so I can't see it clashing with anything in the future e.g. on the "observables" front, if that were to become a thing. If the semantics of `await` were to be extended somehow, then identically and naturally would the semantics of `await.all { ... }`.

On Wed, 27 Nov 2019 at 01:43, Isiah Meadows <[hidden email]> wrote:
Just wanted to drop in and remind people of this by me earlier in the thread: 

The way things are shaping up, it's starting to look like an ad-hoc version of this proposal of mine: 

As I stated earlier, I feel it's premature, especially before we figure out how observables fit into it all.

On Tue, Nov 26, 2019 at 09:48 Naveen Chawla <[hidden email]> wrote:
If I have, as per your examples,

x1 = await||actionAsync1()
x2 = await||actionAsync2(x1) //x1 is undefined here, only resolved on the next "non-parallel-await"

vs adding a line between the two calls:

x1 = await||actionAsync1()
let c;
x2 = await||actionAsync2(x1)

...does the `let c` automatically break the parallel grouping since it's a non-parallel operation (thereby making x1 defined)?

It seems to me like you are doing block logic without blocks, which I think increases the chances of bugs. Also you're not leveraging the existing parallel execution pattern for async functions (which is just to omit the `await`), so it would seem you would be increasing learning overhead. And, you're not really allowing for submitting more sophisticated mixtures of serial async, parallel async and synchronous code for "parallel completion" guarantee, by requiring that parallel calls be "grouped" together in terms of lines of code, almost allowing for nothing beyond the current "Promise.all" pattern, logically. I don't think this is satisfying the original motivation.

For a `awail.all { ... }` block, maybe allowing a "return"/"yield" value could neaten up the block scope separation, but maybe that could be left to the "do" expression if that were adopted. But I don't think it's a big sacrifice if neither exist.


On Tue, 26 Nov 2019 at 14:14, manuelbarzi <[hidden email]> wrote:

OK I'm even more confused now. x1 is surely not a resolved value until all the next "non parallel await" so is it "undefined" until then?

as a `const` x1 does not exist until those parallel awaits `await||` (for p1 and p3) are resolved (same for x3). then p2 is resolved after that.

what it tries to bring is a simplification of syntax.

Could you give an example of what you mean by the `await.all { ... }` block syntax bringing "complexity on returning values assignment, specially when" "about constants (`const`)", as I'm unclear what you are referring to

`const` and `let` are block-scoped, so this is referring to avoid that block notation, and then keep coherent with the possibility to directly infer in current scope references without extra segments / blocks.


On Tue, 26 Nov 2019 at 13:35, manuelbarzi <[hidden email]> wrote:
 
Why not just maximally preserve current JavaScript for parallel execution, just by omitting `await` in multiple async calls, simply wrapping it in an `await.all` block to ensure completion before code continues past the block. This surely is the more straightforward way to satisfy the same goals?

because wrapping it an `await.all` on the one hand explicitly groups promises, but brings complexity on returning values assignment, specially when is about constants (`const`). so, if you avoid blocks, just marking parallel awaits with, for example, a suffix `await||`, or whatever other more convenient way, you can just write the code in series as normally, and avoid that complexity. the transpiler would just require to group the consecutive marked parallel awaits (`await||`) into a Promise.all() and that's it. following i reproduce the demo before:

```
// NOTE p? = function call that returns a promise (? = just and index)
// NOTE s? = function call that runs synchronously and returns a value (? = just and index)

const x0 = await p0()

const x11 = s11() // sync code in-the-middle

const x1 = await || p1(x0)
const x3 = await || p3(x11)
const x2 = await p2(x1)
const x10 = await p10(x2x3)

const x12 = s12() // sync code in-the-middle

const x4 = await || p4(x1x2)
const x5 = await || p5(x2x3x12)
const x6 = await p6(x4x5x10)

const x7 = await || p7(x4x6)
const x9 = await || p9(x5x6)
const x8 = await p8(x6x7)

await p11(x8x9)

// it would resolve a tree of parallel and series like following with traditional promises

p0
    .then(x0 => {
        const x11 = f11()

        return Promise.all([p1(x0), p3(x11)])
            .then((x1x3=>
                p2(x1)
                    .then(x2 =>
                        p10(x2x3)
                            .then(x10 => {
                                const x12 = s12()

                                return Promise.all([p4(x1x2), p5(x2x3x12)])
                                    .then((x4x5=>
                                        p6(x4x5x10)
                                            .then(x6 => Promise.all([p7(x4x6), p9(x5x6)])
                                                .then((x7x9=> p8(x6x7)
                                                    .then(x8 => p11(x8x9))
                                                )
                                            )
                                    )
                            })
                    )
            )
    })
```
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
--

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

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

Jacob Bloom
> It seems to me like you are doing block logic without blocks, which I think
> increases the chances of bugs.

I agree. Without curly braces, it's not always clear when the parallel
code is guaranteed to have executed by. The first version of my
proposal did something similar:

```javascript
const a = await|| doSomethingAsync();
const b = await|| doSomethingElseAsync();
const [aValue, bValue] = await async.all;
```

...where `async.all` represents something like
`Promise.all(promises)`. The problem is, if you forget the `await
async.all` then your promises never execute (within the function), so
accidentally using `await||` instead of `await` would have the
opposite of the intended effect.

`async { await|| ... }` sidesteps both of these issues: it makes it
clear when the promises have all settled by, and if `await` isn't
allowed in the curly brackets then it avoids the "wrong operator"
confusion issue as well.

> you're not leveraging the existing parallel execution pattern for async
> functions (which is just to omit the `await`), so it would seem you
> would be increasing learning overhead.

If `await||` (or whatever) is learned in conjunction with `async {}`
blocks, and is only allowed within them, then it just becomes "this is
the syntax for parallelism." And as already stated, you'd need an
explicit marker for which expressions are being awaited in parallel
for any reasonable transpilation.

> `const` and `let` are block-scoped, so this is referring to avoid that block
> notation, and then keep coherent with the possibility to directly infer in
> current scope references without extra segments / blocks.

Yeah, it'd be nice to have a syntax that doesn't create a block scope.
But that strikes me as less important than making it obvious where the
nondeterminism is.

I assume making these braces not create a block scope is unacceptable.
And using an alternative bracket (maybe `async [ await|| foo ]`) would
be too different from everything else in the language and might read
as an array, which discourages using non-expression statements inside
it



On Wed, Nov 27, 2019 at 9:57 AM Naveen Chawla <[hidden email]> wrote:

>
> I don't know that an `await.all { ... }` block would be premature, especially since it's straightforward, so I can't see it clashing with anything in the future e.g. on the "observables" front, if that were to become a thing. If the semantics of `await` were to be extended somehow, then identically and naturally would the semantics of `await.all { ... }`.
>
> On Wed, 27 Nov 2019 at 01:43, Isiah Meadows <[hidden email]> wrote:
>>
>> Just wanted to drop in and remind people of this by me earlier in the thread:
>> https://esdiscuss.org/topic/proposal-await-all-for-parallelism#content-10
>>
>> The way things are shaping up, it's starting to look like an ad-hoc version of this proposal of mine:
>> https://github.com/isiahmeadows/non-linear-proposal
>>
>> As I stated earlier, I feel it's premature, especially before we figure out how observables fit into it all.
>>
>> On Tue, Nov 26, 2019 at 09:48 Naveen Chawla <[hidden email]> wrote:
>>>
>>> If I have, as per your examples,
>>>
>>> x1 = await||actionAsync1()
>>> x2 = await||actionAsync2(x1) //x1 is undefined here, only resolved on the next "non-parallel-await"
>>>
>>> vs adding a line between the two calls:
>>>
>>> x1 = await||actionAsync1()
>>> let c;
>>> x2 = await||actionAsync2(x1)
>>>
>>> ...does the `let c` automatically break the parallel grouping since it's a non-parallel operation (thereby making x1 defined)?
>>>
>>> It seems to me like you are doing block logic without blocks, which I think increases the chances of bugs. Also you're not leveraging the existing parallel execution pattern for async functions (which is just to omit the `await`), so it would seem you would be increasing learning overhead. And, you're not really allowing for submitting more sophisticated mixtures of serial async, parallel async and synchronous code for "parallel completion" guarantee, by requiring that parallel calls be "grouped" together in terms of lines of code, almost allowing for nothing beyond the current "Promise.all" pattern, logically. I don't think this is satisfying the original motivation.
>>>
>>> For a `awail.all { ... }` block, maybe allowing a "return"/"yield" value could neaten up the block scope separation, but maybe that could be left to the "do" expression if that were adopted. But I don't think it's a big sacrifice if neither exist.
>>>
>>>
>>> On Tue, 26 Nov 2019 at 14:14, manuelbarzi <[hidden email]> wrote:
>>>>
>>>>
>>>>> OK I'm even more confused now. x1 is surely not a resolved value until all the next "non parallel await" so is it "undefined" until then?
>>>>
>>>>
>>>> as a `const` x1 does not exist until those parallel awaits `await||` (for p1 and p3) are resolved (same for x3). then p2 is resolved after that.
>>>>
>>>> what it tries to bring is a simplification of syntax.
>>>>
>>>>> Could you give an example of what you mean by the `await.all { ... }` block syntax bringing "complexity on returning values assignment, specially when" "about constants (`const`)", as I'm unclear what you are referring to
>>>>
>>>>
>>>> `const` and `let` are block-scoped, so this is referring to avoid that block notation, and then keep coherent with the possibility to directly infer in current scope references without extra segments / blocks.
>>>>
>>>>>
>>>>> On Tue, 26 Nov 2019 at 13:35, manuelbarzi <[hidden email]> wrote:
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> Why not just maximally preserve current JavaScript for parallel execution, just by omitting `await` in multiple async calls, simply wrapping it in an `await.all` block to ensure completion before code continues past the block. This surely is the more straightforward way to satisfy the same goals?
>>>>>>
>>>>>>
>>>>>> because wrapping it an `await.all` on the one hand explicitly groups promises, but brings complexity on returning values assignment, specially when is about constants (`const`). so, if you avoid blocks, just marking parallel awaits with, for example, a suffix `await||`, or whatever other more convenient way, you can just write the code in series as normally, and avoid that complexity. the transpiler would just require to group the consecutive marked parallel awaits (`await||`) into a Promise.all() and that's it. following i reproduce the demo before:
>>>>>>
>>>>>> ```
>>>>>> // NOTE p? = function call that returns a promise (? = just and index)
>>>>>> // NOTE s? = function call that runs synchronously and returns a value (? = just and index)
>>>>>>
>>>>>> const x0 = await p0()
>>>>>>
>>>>>> const x11 = s11() // sync code in-the-middle
>>>>>>
>>>>>> const x1 = await || p1(x0)
>>>>>> const x3 = await || p3(x11)
>>>>>> const x2 = await p2(x1)
>>>>>> const x10 = await p10(x2, x3)
>>>>>>
>>>>>> const x12 = s12() // sync code in-the-middle
>>>>>>
>>>>>> const x4 = await || p4(x1, x2)
>>>>>> const x5 = await || p5(x2, x3, x12)
>>>>>> const x6 = await p6(x4, x5, x10)
>>>>>>
>>>>>> const x7 = await || p7(x4, x6)
>>>>>> const x9 = await || p9(x5, x6)
>>>>>> const x8 = await p8(x6, x7)
>>>>>>
>>>>>> await p11(x8, x9)
>>>>>>
>>>>>> // it would resolve a tree of parallel and series like following with traditional promises
>>>>>>
>>>>>> p0
>>>>>>     .then(x0 => {
>>>>>>         const x11 = f11()
>>>>>>
>>>>>>         return Promise.all([p1(x0), p3(x11)])
>>>>>>             .then((x1, x3) =>
>>>>>>                 p2(x1)
>>>>>>                     .then(x2 =>
>>>>>>                         p10(x2, x3)
>>>>>>                             .then(x10 => {
>>>>>>                                 const x12 = s12()
>>>>>>
>>>>>>                                 return Promise.all([p4(x1, x2), p5(x2, x3, x12)])
>>>>>>                                     .then((x4, x5) =>
>>>>>>                                         p6(x4, x5, x10)
>>>>>>                                             .then(x6 => Promise.all([p7(x4, x6), p9(x5, x6)])
>>>>>>                                                 .then((x7, x9) => p8(x6, x7)
>>>>>>                                                     .then(x8 => p11(x8, x9))
>>>>>>                                                 )
>>>>>>                                             )
>>>>>>                                     )
>>>>>>                             })
>>>>>>                     )
>>>>>>             )
>>>>>>     })
>>>>>> ```
>>>
>>> _______________________________________________
>>> es-discuss mailing list
>>> [hidden email]
>>> https://mail.mozilla.org/listinfo/es-discuss
>>
>> --
>> -----
>>
>> Isiah Meadows
>> [hidden email]
>> www.isiahmeadows.com
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

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

Naveen Chawla
OK but would `await||` return a promise? If so, then it would seem redundant compared to just omitting the `await`, as it would offer nothing different, and again, something new to learn for the same logical behaviour. Otherwise, it can only really return `undefined`, which would seem inconstent with both using `await` and omitting `await`. Therefore, I would recommend just omitting await inside an await.all block as the pattern for doing “await until all done” parallelism.

On Fri, 13 Dec 2019, 07:48 Jacob Bloom, <[hidden email]> wrote:
> It seems to me like you are doing block logic without blocks, which I think
> increases the chances of bugs.

I agree. Without curly braces, it's not always clear when the parallel
code is guaranteed to have executed by. The first version of my
proposal did something similar:

```javascript
const a = await|| doSomethingAsync();
const b = await|| doSomethingElseAsync();
const [aValue, bValue] = await async.all;
```

...where `async.all` represents something like
`Promise.all(promises)`. The problem is, if you forget the `await
async.all` then your promises never execute (within the function), so
accidentally using `await||` instead of `await` would have the
opposite of the intended effect.

`async { await|| ... }` sidesteps both of these issues: it makes it
clear when the promises have all settled by, and if `await` isn't
allowed in the curly brackets then it avoids the "wrong operator"
confusion issue as well.

> you're not leveraging the existing parallel execution pattern for async
> functions (which is just to omit the `await`), so it would seem you
> would be increasing learning overhead.

If `await||` (or whatever) is learned in conjunction with `async {}`
blocks, and is only allowed within them, then it just becomes "this is
the syntax for parallelism." And as already stated, you'd need an
explicit marker for which expressions are being awaited in parallel
for any reasonable transpilation.

> `const` and `let` are block-scoped, so this is referring to avoid that block
> notation, and then keep coherent with the possibility to directly infer in
> current scope references without extra segments / blocks.

Yeah, it'd be nice to have a syntax that doesn't create a block scope.
But that strikes me as less important than making it obvious where the
nondeterminism is.

I assume making these braces not create a block scope is unacceptable.
And using an alternative bracket (maybe `async [ await|| foo ]`) would
be too different from everything else in the language and might read
as an array, which discourages using non-expression statements inside
it



On Wed, Nov 27, 2019 at 9:57 AM Naveen Chawla <[hidden email]> wrote:
>
> I don't know that an `await.all { ... }` block would be premature, especially since it's straightforward, so I can't see it clashing with anything in the future e.g. on the "observables" front, if that were to become a thing. If the semantics of `await` were to be extended somehow, then identically and naturally would the semantics of `await.all { ... }`.
>
> On Wed, 27 Nov 2019 at 01:43, Isiah Meadows <[hidden email]> wrote:
>>
>> Just wanted to drop in and remind people of this by me earlier in the thread:
>> https://esdiscuss.org/topic/proposal-await-all-for-parallelism#content-10
>>
>> The way things are shaping up, it's starting to look like an ad-hoc version of this proposal of mine:
>> https://github.com/isiahmeadows/non-linear-proposal
>>
>> As I stated earlier, I feel it's premature, especially before we figure out how observables fit into it all.
>>
>> On Tue, Nov 26, 2019 at 09:48 Naveen Chawla <[hidden email]> wrote:
>>>
>>> If I have, as per your examples,
>>>
>>> x1 = await||actionAsync1()
>>> x2 = await||actionAsync2(x1) //x1 is undefined here, only resolved on the next "non-parallel-await"
>>>
>>> vs adding a line between the two calls:
>>>
>>> x1 = await||actionAsync1()
>>> let c;
>>> x2 = await||actionAsync2(x1)
>>>
>>> ...does the `let c` automatically break the parallel grouping since it's a non-parallel operation (thereby making x1 defined)?
>>>
>>> It seems to me like you are doing block logic without blocks, which I think increases the chances of bugs. Also you're not leveraging the existing parallel execution pattern for async functions (which is just to omit the `await`), so it would seem you would be increasing learning overhead. And, you're not really allowing for submitting more sophisticated mixtures of serial async, parallel async and synchronous code for "parallel completion" guarantee, by requiring that parallel calls be "grouped" together in terms of lines of code, almost allowing for nothing beyond the current "Promise.all" pattern, logically. I don't think this is satisfying the original motivation.
>>>
>>> For a `awail.all { ... }` block, maybe allowing a "return"/"yield" value could neaten up the block scope separation, but maybe that could be left to the "do" expression if that were adopted. But I don't think it's a big sacrifice if neither exist.
>>>
>>>
>>> On Tue, 26 Nov 2019 at 14:14, manuelbarzi <[hidden email]> wrote:
>>>>
>>>>
>>>>> OK I'm even more confused now. x1 is surely not a resolved value until all the next "non parallel await" so is it "undefined" until then?
>>>>
>>>>
>>>> as a `const` x1 does not exist until those parallel awaits `await||` (for p1 and p3) are resolved (same for x3). then p2 is resolved after that.
>>>>
>>>> what it tries to bring is a simplification of syntax.
>>>>
>>>>> Could you give an example of what you mean by the `await.all { ... }` block syntax bringing "complexity on returning values assignment, specially when" "about constants (`const`)", as I'm unclear what you are referring to
>>>>
>>>>
>>>> `const` and `let` are block-scoped, so this is referring to avoid that block notation, and then keep coherent with the possibility to directly infer in current scope references without extra segments / blocks.
>>>>
>>>>>
>>>>> On Tue, 26 Nov 2019 at 13:35, manuelbarzi <[hidden email]> wrote:
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> Why not just maximally preserve current JavaScript for parallel execution, just by omitting `await` in multiple async calls, simply wrapping it in an `await.all` block to ensure completion before code continues past the block. This surely is the more straightforward way to satisfy the same goals?
>>>>>>
>>>>>>
>>>>>> because wrapping it an `await.all` on the one hand explicitly groups promises, but brings complexity on returning values assignment, specially when is about constants (`const`). so, if you avoid blocks, just marking parallel awaits with, for example, a suffix `await||`, or whatever other more convenient way, you can just write the code in series as normally, and avoid that complexity. the transpiler would just require to group the consecutive marked parallel awaits (`await||`) into a Promise.all() and that's it. following i reproduce the demo before:
>>>>>>
>>>>>> ```
>>>>>> // NOTE p? = function call that returns a promise (? = just and index)
>>>>>> // NOTE s? = function call that runs synchronously and returns a value (? = just and index)
>>>>>>
>>>>>> const x0 = await p0()
>>>>>>
>>>>>> const x11 = s11() // sync code in-the-middle
>>>>>>
>>>>>> const x1 = await || p1(x0)
>>>>>> const x3 = await || p3(x11)
>>>>>> const x2 = await p2(x1)
>>>>>> const x10 = await p10(x2, x3)
>>>>>>
>>>>>> const x12 = s12() // sync code in-the-middle
>>>>>>
>>>>>> const x4 = await || p4(x1, x2)
>>>>>> const x5 = await || p5(x2, x3, x12)
>>>>>> const x6 = await p6(x4, x5, x10)
>>>>>>
>>>>>> const x7 = await || p7(x4, x6)
>>>>>> const x9 = await || p9(x5, x6)
>>>>>> const x8 = await p8(x6, x7)
>>>>>>
>>>>>> await p11(x8, x9)
>>>>>>
>>>>>> // it would resolve a tree of parallel and series like following with traditional promises
>>>>>>
>>>>>> p0
>>>>>>     .then(x0 => {
>>>>>>         const x11 = f11()
>>>>>>
>>>>>>         return Promise.all([p1(x0), p3(x11)])
>>>>>>             .then((x1, x3) =>
>>>>>>                 p2(x1)
>>>>>>                     .then(x2 =>
>>>>>>                         p10(x2, x3)
>>>>>>                             .then(x10 => {
>>>>>>                                 const x12 = s12()
>>>>>>
>>>>>>                                 return Promise.all([p4(x1, x2), p5(x2, x3, x12)])
>>>>>>                                     .then((x4, x5) =>
>>>>>>                                         p6(x4, x5, x10)
>>>>>>                                             .then(x6 => Promise.all([p7(x4, x6), p9(x5, x6)])
>>>>>>                                                 .then((x7, x9) => p8(x6, x7)
>>>>>>                                                     .then(x8 => p11(x8, x9))
>>>>>>                                                 )
>>>>>>                                             )
>>>>>>                                     )
>>>>>>                             })
>>>>>>                     )
>>>>>>             )
>>>>>>     })
>>>>>> ```
>>>
>>> _______________________________________________
>>> es-discuss mailing list
>>> [hidden email]
>>> https://mail.mozilla.org/listinfo/es-discuss
>>
>> --
>> -----
>>
>> Isiah Meadows
>> [hidden email]
>> www.isiahmeadows.com
>
> _______________________________________________
> es-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
123