async/await -> await/async: a simpler, less error-prone async syntax

classic Classic list List threaded Threaded
25 messages Options
12
Reply | Threaded
Open this post in threaded view
|

async/await -> await/async: a simpler, less error-prone async syntax

voracity
Sorry for making this request now (rather than when async/await was first being formulated), but real-world use has led me to think some things could benefit from a slightly different syntax. I also apologise if this has been raised/discussed before, I wasn't able to find it. The proposal is most quickly explained with an example.

Current:

class RemoteService {
    async init() { ... }
    async setProp(id, val) { ...; return this }
    async getProp(id) { return ... }
    async runInBackground() { ... }
}

async function remoteExample() {
    let remote = new RemoteService();
    await remote.init();
    await (await remote.setProp('a', 1)).setProp('b', 2);
    remote.runInBackground();
    let val = await remote.getProp('a');
    return val;
}

Proposed:

class RemoteService {
    await init() { ... }
    await setProp(id, val) { ...; return this }
    await getProp(id) { return ... }
    await runInBackground() { ... }
}

await function remoteExample() {
    let remote = new RemoteService();
    remote.init();
    remote.setProp('a', 1).setProp('b', 2);
    async remote.runInBackground();
    let val = remote.getProp('a');
    return val;
}

Why:

Running things in a genuinely asynchronous way is actually *unusual*. The current async/await syntax (which I think should still stay) is the exact reverse of what fits the probable use cases. Missing the 'await' keyword (particularly on functions/methods that don't return a value) causes all sorts of hard to track down bugs. I myself didn't realise this until I (and others I work with) started making intense use of async/await.

The new proposed syntax above is obviously much simpler and would be very hard to get wrong. By contrast, the original syntax has proven surprisingly difficult to get consistently right. Even now with quite a lot of practice, I'm occasionally forgetting an await here and there. In addition, patterns like chaining get ugly *very* quickly. (Although I guess that could also be fixed with a method-friendly async call syntax.)

If the proposed syntax were available in addition to the current syntax, you could apply whichever keyword in the function declaration is most likely applicable at call sites. e.g. runInBackground could be declared 'async' rather than 'await', if you normally expect it to be run in the background.


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

Re: async/await -> await/async: a simpler, less error-prone async syntax

T.J. Crowder-2
I understand the motivation, but I have to disagree with the proposal
for multiple reasons:

1. It adds too much confusion to the existing syntax.

2. If the calling code needs to call one of these new-style `await`
functions and access its promise (rather than `await`ing it), it
doesn't have a way to do that; `async`/`await` doesn't have full
coverage of all patterns (yet, possibly never), sometimes you still
need to access the actual promise (for instance, to feed into
`Promise.all` or `Promise.race`). (Obviously you could add something
to make it possible to access the promise.)

3. Having a clear indicator in the source saying where the async
boundaries are is useful for code correctness. With the new syntax, I
have no idea of the temporal sequence of this code:

```js
let x = foo();
let y = bar();
```

...without going and looking at the declarations of `foo` and `bar`.
With current syntax, it's clear when there's an async break in the
flow. I think the phrase is "prefer explicit to implicit" or something
like that.

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

Re: async/await -> await/async: a simpler, less error-prone async syntax

Naveen Chawla
The crux of your idea is: let any calls of an async function inside an async function automatically await unless explicity instructed to run "in the background". I like the idea. I just wouldn't use the words "await" and "async" for it, because they are used as currently in C#.

On the last disagreement by T.J Crowder: 1. it's an alternative (perhaps with different words), not necessarily in conjunction with the existing syntax. 2. The promise would be accessible via e.g. `const promise = background remote.runInBackground();` 3. If a presumption is made that in an async function, other async function calls are automatically awaited unless prefixed with `background`, I'm not sure about the value of having a "clear indicator" of which function calls are sync and which are async - in normal use I wouldn't care. I just want their results and if I want to make a function async (by adding a server call, etc.), I can do so transparently without breaking my code. I agree with the thrust of this proposal.

The horse has bolted on `await` `async`, but maybe with different keywords, e.g. `asyncauto` and `background`, I would definitely use this new syntax instead of `await` `async` (which I use currently), for the reasons stated in the original post. Bug minimization is a big factor in the features I use, and should be THE driving factor in introducing new features.

The OP has stated this idea upon extensive real world use, and I appreciate that. I would definitely support the introduction of e.g. `asyncauto` and `background`, and would definitely use it immediately in all cases instead of `async` `await` if introduced.

On Sun, 3 Dec 2017 at 17:13 T.J. Crowder <[hidden email]> wrote:
I understand the motivation, but I have to disagree with the proposal
for multiple reasons:

1. It adds too much confusion to the existing syntax.

2. If the calling code needs to call one of these new-style `await`
functions and access its promise (rather than `await`ing it), it
doesn't have a way to do that; `async`/`await` doesn't have full
coverage of all patterns (yet, possibly never), sometimes you still
need to access the actual promise (for instance, to feed into
`Promise.all` or `Promise.race`). (Obviously you could add something
to make it possible to access the promise.)

3. Having a clear indicator in the source saying where the async
boundaries are is useful for code correctness. With the new syntax, I
have no idea of the temporal sequence of this code:

```js
let x = foo();
let y = bar();
```

...without going and looking at the declarations of `foo` and `bar`.
With current syntax, it's clear when there's an async break in the
flow. I think the phrase is "prefer explicit to implicit" or something
like that.

-- T.J. Crowder
_______________________________________________
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: async/await -> await/async: a simpler, less error-prone async syntax

voracity
Thanks Naveen, I agree, potentially in full.

Let me address T.J. Crowder's objections out of order (2,3,1), as the response will be clearer that way.

2. As Naveen noted, this is a use case that I did (and very much intended to) handle in the proposal by having a keyword at the call site. Namely: `let promise = async remote.runInBackground();`.

3. I think the principle "prefer explicit to implicit" argues in *favour* of the proposal (and against the existing syntax). You gave the example:

```js
let x = foo();
let y = bar();
```

Imperative language conventions imply the second statement only executes after the first is fully complete. But if `foo` and `bar` are defined as async functions, the second statement likely starts executing before the *real* work being done in the first statement is complete. The proposed syntax restores the normal imperative conventions, and requires one to be explicit when code departs from those conventions. e.g.:

```js
let p = async baz(); // We're explicit that this doesn't work like every other sync function, it just returns a promise!
let x = foo(); // This could be an await function or an ordinary sync function. We don't need to care (outside of atomics,etc.)
let y = bar(x); // Ditto
```

If you're really committed to being explicit, then we should require the coder to specify how they want *every* call to an async function to be handled. e.g.:

```js
let p = async baz(); // Explicit
let x = await foo(); // Explicit
let y = await bar(x); // Explicit
```

But this seems needlessly verbose.

1. I'm not committed to keeping the `async` and `await` keywords. My original thoughts on this were much like Naveen's proposed keywords. I was initially using `awaitalways` for `await` in the function declaration and `trap` for `async` at the call site, but if we do want to use different keywords, I think I prefer `awaitauto` and `background`. (`awaitauto` rather than `asyncauto` because I think it's very important to be clear about what the default call behaviour becomes.)

However, I later realised that the keyword reuse will only be confusing to someone who has only learned the existing (essentially incomplete) async/await syntax. Someone entirely *new* to the language may actually find the completed symmetry of the proposal *less* confusing. The explanation for a new JS developer might run as follows:

===
*Asynchronous Functions*
An "asynchronous function" is any function that returns a `Promise`.

`async` and `await` control how an asynchronous function is executed. Prefixing a call to an asynchronous function with `async` will cause it to be run in an asynchronous thread, and will return the `Promise`. Prefixing a function call with `await` will cause execution of the current function to be suspended until the asynchronous function has completed (successfully or otherwise), potentially returning a value computed by the function. Specifying how asynchronous functions should be called every time is cumbersome and error-prone, so you can specify the default approach to calling the function in the function declaration. Thus:

```js
async function foo() {}
```

specifies that all calls to the function `foo` will use the `async` method by default and

```js
await function bar() {}
```

specifies that all calls to the function `bar` will use the `await` method by default. In either case, you can override the default and force the call behaviour by prefixing the call with either `await` or `async`.
===

And I think this possibly makes things far clearer than any description I've yet seen, not because it was especially eloquent, but simply because the missing pieces have now been filled in.

Nonetheless, I could be convinced otherwise, and I'd be more than willing to accept Naveen's suggestions for different keywords.

Cheers,
Steven


On 4 December 2017 at 16:38, Naveen Chawla <[hidden email]> wrote:
The crux of your idea is: let any calls of an async function inside an async function automatically await unless explicity instructed to run "in the background". I like the idea. I just wouldn't use the words "await" and "async" for it, because they are used as currently in C#.

On the last disagreement by T.J Crowder: 1. it's an alternative (perhaps with different words), not necessarily in conjunction with the existing syntax. 2. The promise would be accessible via e.g. `const promise = background remote.runInBackground();` 3. If a presumption is made that in an async function, other async function calls are automatically awaited unless prefixed with `background`, I'm not sure about the value of having a "clear indicator" of which function calls are sync and which are async - in normal use I wouldn't care. I just want their results and if I want to make a function async (by adding a server call, etc.), I can do so transparently without breaking my code. I agree with the thrust of this proposal.

The horse has bolted on `await` `async`, but maybe with different keywords, e.g. `asyncauto` and `background`, I would definitely use this new syntax instead of `await` `async` (which I use currently), for the reasons stated in the original post. Bug minimization is a big factor in the features I use, and should be THE driving factor in introducing new features.

The OP has stated this idea upon extensive real world use, and I appreciate that. I would definitely support the introduction of e.g. `asyncauto` and `background`, and would definitely use it immediately in all cases instead of `async` `await` if introduced.

On Sun, 3 Dec 2017 at 17:13 T.J. Crowder <[hidden email]> wrote:
I understand the motivation, but I have to disagree with the proposal
for multiple reasons:

1. It adds too much confusion to the existing syntax.

2. If the calling code needs to call one of these new-style `await`
functions and access its promise (rather than `await`ing it), it
doesn't have a way to do that; `async`/`await` doesn't have full
coverage of all patterns (yet, possibly never), sometimes you still
need to access the actual promise (for instance, to feed into
`Promise.all` or `Promise.race`). (Obviously you could add something
to make it possible to access the promise.)

3. Having a clear indicator in the source saying where the async
boundaries are is useful for code correctness. With the new syntax, I
have no idea of the temporal sequence of this code:

```js
let x = foo();
let y = bar();
```

...without going and looking at the declarations of `foo` and `bar`.
With current syntax, it's clear when there's an async break in the
flow. I think the phrase is "prefer explicit to implicit" or something
like that.

-- T.J. Crowder
_______________________________________________
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: async/await -> await/async: a simpler, less error-prone async syntax

voracity
Oh, and just to be clear, there would still need to be an explanation for why you need to attach `async` or `await` to any function that contains a call to an asynchronous function. I assume that requirement exists for performance reasons --- I have suggested that requirement be dropped elsewhere, but understand that may not be practical.

On 4 December 2017 at 23:52, Steven Mascaro <[hidden email]> wrote:
Thanks Naveen, I agree, potentially in full.

Let me address T.J. Crowder's objections out of order (2,3,1), as the response will be clearer that way.

2. As Naveen noted, this is a use case that I did (and very much intended to) handle in the proposal by having a keyword at the call site. Namely: `let promise = async remote.runInBackground();`.

3. I think the principle "prefer explicit to implicit" argues in *favour* of the proposal (and against the existing syntax). You gave the example:

```js
let x = foo();
let y = bar();
```

Imperative language conventions imply the second statement only executes after the first is fully complete. But if `foo` and `bar` are defined as async functions, the second statement likely starts executing before the *real* work being done in the first statement is complete. The proposed syntax restores the normal imperative conventions, and requires one to be explicit when code departs from those conventions. e.g.:

```js
let p = async baz(); // We're explicit that this doesn't work like every other sync function, it just returns a promise!
let x = foo(); // This could be an await function or an ordinary sync function. We don't need to care (outside of atomics,etc.)
let y = bar(x); // Ditto
```

If you're really committed to being explicit, then we should require the coder to specify how they want *every* call to an async function to be handled. e.g.:

```js
let p = async baz(); // Explicit
let x = await foo(); // Explicit
let y = await bar(x); // Explicit
```

But this seems needlessly verbose.

1. I'm not committed to keeping the `async` and `await` keywords. My original thoughts on this were much like Naveen's proposed keywords. I was initially using `awaitalways` for `await` in the function declaration and `trap` for `async` at the call site, but if we do want to use different keywords, I think I prefer `awaitauto` and `background`. (`awaitauto` rather than `asyncauto` because I think it's very important to be clear about what the default call behaviour becomes.)

However, I later realised that the keyword reuse will only be confusing to someone who has only learned the existing (essentially incomplete) async/await syntax. Someone entirely *new* to the language may actually find the completed symmetry of the proposal *less* confusing. The explanation for a new JS developer might run as follows:

===
*Asynchronous Functions*
An "asynchronous function" is any function that returns a `Promise`.

`async` and `await` control how an asynchronous function is executed. Prefixing a call to an asynchronous function with `async` will cause it to be run in an asynchronous thread, and will return the `Promise`. Prefixing a function call with `await` will cause execution of the current function to be suspended until the asynchronous function has completed (successfully or otherwise), potentially returning a value computed by the function. Specifying how asynchronous functions should be called every time is cumbersome and error-prone, so you can specify the default approach to calling the function in the function declaration. Thus:

```js
async function foo() {}
```

specifies that all calls to the function `foo` will use the `async` method by default and

```js
await function bar() {}
```

specifies that all calls to the function `bar` will use the `await` method by default. In either case, you can override the default and force the call behaviour by prefixing the call with either `await` or `async`.
===

And I think this possibly makes things far clearer than any description I've yet seen, not because it was especially eloquent, but simply because the missing pieces have now been filled in.

Nonetheless, I could be convinced otherwise, and I'd be more than willing to accept Naveen's suggestions for different keywords.

Cheers,
Steven


On 4 December 2017 at 16:38, Naveen Chawla <[hidden email]> wrote:
The crux of your idea is: let any calls of an async function inside an async function automatically await unless explicity instructed to run "in the background". I like the idea. I just wouldn't use the words "await" and "async" for it, because they are used as currently in C#.

On the last disagreement by T.J Crowder: 1. it's an alternative (perhaps with different words), not necessarily in conjunction with the existing syntax. 2. The promise would be accessible via e.g. `const promise = background remote.runInBackground();` 3. If a presumption is made that in an async function, other async function calls are automatically awaited unless prefixed with `background`, I'm not sure about the value of having a "clear indicator" of which function calls are sync and which are async - in normal use I wouldn't care. I just want their results and if I want to make a function async (by adding a server call, etc.), I can do so transparently without breaking my code. I agree with the thrust of this proposal.

The horse has bolted on `await` `async`, but maybe with different keywords, e.g. `asyncauto` and `background`, I would definitely use this new syntax instead of `await` `async` (which I use currently), for the reasons stated in the original post. Bug minimization is a big factor in the features I use, and should be THE driving factor in introducing new features.

The OP has stated this idea upon extensive real world use, and I appreciate that. I would definitely support the introduction of e.g. `asyncauto` and `background`, and would definitely use it immediately in all cases instead of `async` `await` if introduced.

On Sun, 3 Dec 2017 at 17:13 T.J. Crowder <[hidden email]> wrote:
I understand the motivation, but I have to disagree with the proposal
for multiple reasons:

1. It adds too much confusion to the existing syntax.

2. If the calling code needs to call one of these new-style `await`
functions and access its promise (rather than `await`ing it), it
doesn't have a way to do that; `async`/`await` doesn't have full
coverage of all patterns (yet, possibly never), sometimes you still
need to access the actual promise (for instance, to feed into
`Promise.all` or `Promise.race`). (Obviously you could add something
to make it possible to access the promise.)

3. Having a clear indicator in the source saying where the async
boundaries are is useful for code correctness. With the new syntax, I
have no idea of the temporal sequence of this code:

```js
let x = foo();
let y = bar();
```

...without going and looking at the declarations of `foo` and `bar`.
With current syntax, it's clear when there's an async break in the
flow. I think the phrase is "prefer explicit to implicit" or something
like that.

-- T.J. Crowder
_______________________________________________
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: async/await -> await/async: a simpler, less error-prone async syntax

T.J. Crowder-2
In reply to this post by voracity
On Mon, Dec 4, 2017 at 12:52 PM, Steven Mascaro <[hidden email]> wrote:
> 3. I think the principle "prefer explicit to implicit" argues in
> *favour* of the proposal (and against the existing syntax).
> You gave the example:
>
> ```js
> let x = foo();
> let y = bar();
> ```
>
> Imperative language conventions imply the second statement only
> executes after the first is fully complete. But if `foo` and
> `bar` are defined as async functions, the second statement likely
> starts executing before the *real* work being done in the first
> statement is complete.

I understand what you're saying there, I just disagree. I said I know the **temporal** sequence when looking at that code, and that's what I'm saying we should continue to be explicit about when it isn't "this then that" without the job yielding and potentially another job being scheduled in the meantime. That kind of yield for other work is a big deal and should, in my view, be flagged. I get the desire for it to represent the *logical* sequence instead without flags, I just think it's too late to do that to JavaScript. I keep flirting with the idea of a purely-async or async-is-the-assumption language and this is part of why, but I don't see retrofitting JavaScript to be that language. Perhaps that's pessimistic of me, but when these two functions have completely different temporal semantics, I think it's time for a new language:

```js
(await () => {
    foo();
    bar();
})();
(() => {
    foo();
    bar();
})();
```

Maybe before the current `async`/`await` were added, but...

> If you're really committed to being explicit, then we should require
> the coder to specify how they want *every* call to an async function
> to be handled.

Clearly not, that's just reductio ad absurdum. We have a massively well-established "If it's a plain call, it happens in the same job in linear sequence" precedent. For me (and we just disagree on this, which is fine), if it's going to be a yield back to the queue, it needs signifying.

Those are my $0.02, FWIW. Maybe I'm being old-fashioned. :-)

-- T.J. Crowder

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

Re: async/await -> await/async: a simpler, less error-prone async syntax

T.J. Crowder-2
Apologies for the double-post. I meant to end with:

**All of that said**, you are talking about opt-in behavior. And that makes it an easier sell in many ways. Doesn't make it less confusing having two competing async syntaxes, and I think that's a major issue, but with the opt-in at least I know (if I'm paying attention) that when I'm looking at what seems like a sequence of calls in temporal sequence, I know it *isn't*, it's logical sequence instead. :-)

-- T.J. Crowder

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

Re: async/await -> await/async: a simpler, less error-prone async syntax

Bob Myers
In reply to this post by T.J. Crowder-2
It might be useful to consider the `ControlFlow` notion used in writing selenium tests. 
All the steps in a test routine, which appear synchronous, actually implicitly introduce the equivalent of promise/then structures.
They took this approach because almost all such integration tests are asynchronous at their core, and they did not want to force people to write huge sequences of `then` calls.

It turns out that this approach has a number of problems.
As a result, future versions of selenium will no longer support it.
Test writers will be asked to write `await` where needed.

For more information on this, see 

--
Bob

On Mon, Dec 4, 2017 at 7:27 PM, T.J. Crowder <[hidden email]> wrote:
On Mon, Dec 4, 2017 at 12:52 PM, Steven Mascaro <[hidden email]> wrote:
> 3. I think the principle "prefer explicit to implicit" argues in
> *favour* of the proposal (and against the existing syntax).
> You gave the example:
>
> ```js
> let x = foo();
> let y = bar();
> ```
>
> Imperative language conventions imply the second statement only
> executes after the first is fully complete. But if `foo` and
> `bar` are defined as async functions, the second statement likely
> starts executing before the *real* work being done in the first
> statement is complete.

I understand what you're saying there, I just disagree. I said I know the **temporal** sequence when looking at that code, and that's what I'm saying we should continue to be explicit about when it isn't "this then that" without the job yielding and potentially another job being scheduled in the meantime. That kind of yield for other work is a big deal and should, in my view, be flagged. I get the desire for it to represent the *logical* sequence instead without flags, I just think it's too late to do that to JavaScript. I keep flirting with the idea of a purely-async or async-is-the-assumption language and this is part of why, but I don't see retrofitting JavaScript to be that language. Perhaps that's pessimistic of me, but when these two functions have completely different temporal semantics, I think it's time for a new language:

```js
(await () => {
    foo();
    bar();
})();
(() => {
    foo();
    bar();
})();
```

Maybe before the current `async`/`await` were added, but...

> If you're really committed to being explicit, then we should require
> the coder to specify how they want *every* call to an async function
> to be handled.

Clearly not, that's just reductio ad absurdum. We have a massively well-established "If it's a plain call, it happens in the same job in linear sequence" precedent. For me (and we just disagree on this, which is fine), if it's going to be a yield back to the queue, it needs signifying.

Those are my $0.02, FWIW. Maybe I'm being old-fashioned. :-)

-- T.J. Crowder

_______________________________________________
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: async/await -> await/async: a simpler, less error-prone async syntax

T.J. Crowder-2
On Mon, Dec 4, 2017 at 2:37 PM, Bob Myers <[hidden email]> wrote:
>
> It turns out that this approach has a number of problems.
> As a result, future versions of selenium will no longer support it.
> Test writers will be asked to write `await` where needed.

Were there problems other than the complexity of maintaining the
promise manager tool and issues with debuggability? Neither of those
seems like it would be an issue with Steven's proposal...

(I just realized for the first time, Bob, that your initials are RTM.
I love it. You should adopt Frank as a second middle name. ;-) )

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

Re: async/await -> await/async: a simpler, less error-prone async syntax

Isiah Meadows-2
Am I misunderstanding something about this proposal that it's substantially any different from `.then` or immediately invoked async functions?

```js
// Original
await function foo() {
    const bar = async baz()
    use(bar)
}

// What I'm reading
function foo() {
    ;(async () => {
        const bar = await baz()
        use(bar)
    })()
}
function foo() {
    try {
        Promise.resolve(baz())
        .then(bar => { use(bar) })
    } catch (e) {
        Promise.reject(e)
    }
}
```


On Mon, Dec 4, 2017, 09:54 T.J. Crowder <[hidden email]> wrote:
On Mon, Dec 4, 2017 at 2:37 PM, Bob Myers <[hidden email]> wrote:
>
> It turns out that this approach has a number of problems.
> As a result, future versions of selenium will no longer support it.
> Test writers will be asked to write `await` where needed.

Were there problems other than the complexity of maintaining the
promise manager tool and issues with debuggability? Neither of those
seems like it would be an issue with Steven's proposal...

(I just realized for the first time, Bob, that your initials are RTM.
I love it. You should adopt Frank as a second middle name. ;-) )

-- T.J. Crowder
_______________________________________________
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: async/await -> await/async: a simpler, less error-prone async syntax

Naveen Chawla
Obviously. The whole point of this proposal is that awaiting async functions is automatically _implied_ inside an `autoasync` function, unless an async function is called with a `background` qualifier (which thereby makes it return its promise instead). The OP is right: this is a far less bug prone way to do async programming than `async` `await`, while offering all its functionality, with the added bonus of transparently allowing sync functions to be converted to async functions without fundamentally affecting consumer calling code.

For those who want to be able to do extensive async programming, having this in the language, and using it instead of `await` `async` throughout, is a no-brainer.

Of course, I am qualifying that it must be new keywords, not `await` `async` juggled like in the original post, but that wasn't the thrust of the proposal anyway.

On Mon, 4 Dec 2017 at 21:09 Isiah Meadows <[hidden email]> wrote:
Am I misunderstanding something about this proposal that it's substantially any different from `.then` or immediately invoked async functions?

```js
// Original
await function foo() {
    const bar = async baz()
    use(bar)
}

// What I'm reading
function foo() {
    ;(async () => {
        const bar = await baz()
        use(bar)
    })()
}
function foo() {
    try {
        Promise.resolve(baz())
        .then(bar => { use(bar) })
    } catch (e) {
        Promise.reject(e)
    }
}
```


On Mon, Dec 4, 2017, 09:54 T.J. Crowder <[hidden email]> wrote:
On Mon, Dec 4, 2017 at 2:37 PM, Bob Myers <[hidden email]> wrote:
>
> It turns out that this approach has a number of problems.
> As a result, future versions of selenium will no longer support it.
> Test writers will be asked to write `await` where needed.

Were there problems other than the complexity of maintaining the
promise manager tool and issues with debuggability? Neither of those
seems like it would be an issue with Steven's proposal...

(I just realized for the first time, Bob, that your initials are RTM.
I love it. You should adopt Frank as a second middle name. ;-) )

-- T.J. Crowder
_______________________________________________
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
Reply | Threaded
Open this post in threaded view
|

Re: async/await -> await/async: a simpler, less error-prone async syntax

Naveen Chawla
In reply to this post by voracity
Steven, this is a replacement for `await` `async`, and makes `await` `async` redundant. There is no point teaching `await` `async` if you can do `asyncauto` and `background`, just like there's no point teaching `var` if you can do `let` and `const`. It should be a clean break. Otherwise it introduces a different kind of bug: not remembering which way around the keywords go.

Why I prefer `asyncauto` instead of `awaitauto` is because it still has to be made clear that it's an async function. It is awkward to have `awaitauto function` instead of `asyncauto function`.

On Mon, 4 Dec 2017 at 18:23 Steven Mascaro <[hidden email]> wrote:
Thanks Naveen, I agree, potentially in full.

Let me address T.J. Crowder's objections out of order (2,3,1), as the response will be clearer that way.

2. As Naveen noted, this is a use case that I did (and very much intended to) handle in the proposal by having a keyword at the call site. Namely: `let promise = async remote.runInBackground();`.

3. I think the principle "prefer explicit to implicit" argues in *favour* of the proposal (and against the existing syntax). You gave the example:


```js
let x = foo();
let y = bar();
```

Imperative language conventions imply the second statement only executes after the first is fully complete. But if `foo` and `bar` are defined as async functions, the second statement likely starts executing before the *real* work being done in the first statement is complete. The proposed syntax restores the normal imperative conventions, and requires one to be explicit when code departs from those conventions. e.g.:

```js
let p = async baz(); // We're explicit that this doesn't work like every other sync function, it just returns a promise!
let x = foo(); // This could be an await function or an ordinary sync function. We don't need to care (outside of atomics,etc.)
let y = bar(x); // Ditto
```

If you're really committed to being explicit, then we should require the coder to specify how they want *every* call to an async function to be handled. e.g.:

```js
let p = async baz(); // Explicit
let x = await foo(); // Explicit
let y = await bar(x); // Explicit
```

But this seems needlessly verbose.

1. I'm not committed to keeping the `async` and `await` keywords. My original thoughts on this were much like Naveen's proposed keywords. I was initially using `awaitalways` for `await` in the function declaration and `trap` for `async` at the call site, but if we do want to use different keywords, I think I prefer `awaitauto` and `background`. (`awaitauto` rather than `asyncauto` because I think it's very important to be clear about what the default call behaviour becomes.)

However, I later realised that the keyword reuse will only be confusing to someone who has only learned the existing (essentially incomplete) async/await syntax. Someone entirely *new* to the language may actually find the completed symmetry of the proposal *less* confusing. The explanation for a new JS developer might run as follows:

===
*Asynchronous Functions*
An "asynchronous function" is any function that returns a `Promise`.

`async` and `await` control how an asynchronous function is executed. Prefixing a call to an asynchronous function with `async` will cause it to be run in an asynchronous thread, and will return the `Promise`. Prefixing a function call with `await` will cause execution of the current function to be suspended until the asynchronous function has completed (successfully or otherwise), potentially returning a value computed by the function. Specifying how asynchronous functions should be called every time is cumbersome and error-prone, so you can specify the default approach to calling the function in the function declaration. Thus:

```js
async function foo() {}
```

specifies that all calls to the function `foo` will use the `async` method by default and

```js
await function bar() {}
```

specifies that all calls to the function `bar` will use the `await` method by default. In either case, you can override the default and force the call behaviour by prefixing the call with either `await` or `async`.
===

And I think this possibly makes things far clearer than any description I've yet seen, not because it was especially eloquent, but simply because the missing pieces have now been filled in.

Nonetheless, I could be convinced otherwise, and I'd be more than willing to accept Naveen's suggestions for different keywords.

Cheers,
Steven


On 4 December 2017 at 16:38, Naveen Chawla <[hidden email]> wrote:
The crux of your idea is: let any calls of an async function inside an async function automatically await unless explicity instructed to run "in the background". I like the idea. I just wouldn't use the words "await" and "async" for it, because they are used as currently in C#.

On the last disagreement by T.J Crowder: 1. it's an alternative (perhaps with different words), not necessarily in conjunction with the existing syntax. 2. The promise would be accessible via e.g. `const promise = background remote.runInBackground();` 3. If a presumption is made that in an async function, other async function calls are automatically awaited unless prefixed with `background`, I'm not sure about the value of having a "clear indicator" of which function calls are sync and which are async - in normal use I wouldn't care. I just want their results and if I want to make a function async (by adding a server call, etc.), I can do so transparently without breaking my code. I agree with the thrust of this proposal.

The horse has bolted on `await` `async`, but maybe with different keywords, e.g. `asyncauto` and `background`, I would definitely use this new syntax instead of `await` `async` (which I use currently), for the reasons stated in the original post. Bug minimization is a big factor in the features I use, and should be THE driving factor in introducing new features.

The OP has stated this idea upon extensive real world use, and I appreciate that. I would definitely support the introduction of e.g. `asyncauto` and `background`, and would definitely use it immediately in all cases instead of `async` `await` if introduced.

On Sun, 3 Dec 2017 at 17:13 T.J. Crowder <[hidden email]> wrote:
I understand the motivation, but I have to disagree with the proposal
for multiple reasons:

1. It adds too much confusion to the existing syntax.

2. If the calling code needs to call one of these new-style `await`
functions and access its promise (rather than `await`ing it), it
doesn't have a way to do that; `async`/`await` doesn't have full
coverage of all patterns (yet, possibly never), sometimes you still
need to access the actual promise (for instance, to feed into
`Promise.all` or `Promise.race`). (Obviously you could add something
to make it possible to access the promise.)

3. Having a clear indicator in the source saying where the async
boundaries are is useful for code correctness. With the new syntax, I
have no idea of the temporal sequence of this code:

```js
let x = foo();
let y = bar();
```

...without going and looking at the declarations of `foo` and `bar`.
With current syntax, it's clear when there's an async break in the
flow. I think the phrase is "prefer explicit to implicit" or something
like that.

-- T.J. Crowder
_______________________________________________
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: async/await -> await/async: a simpler, less error-prone async syntax

Alexander Jones
In reply to this post by Naveen Chawla
with the added bonus of transparently allowing sync functions to be converted to async functions without fundamentally affecting consumer calling code.

It does fundamentally affect calling code if the calling code reaches outside of its local variables. The state of the world around you might change any time you await and give control back to the event loop.

On Mon, 4 Dec 2017 at 19:16, Naveen Chawla <[hidden email]> wrote:
Obviously. The whole point of this proposal is that awaiting async functions is automatically _implied_ inside an `autoasync` function, unless an async function is called with a `background` qualifier (which thereby makes it return its promise instead). The OP is right: this is a far less bug prone way to do async programming than `async` `await`, while offering all its functionality, with the added bonus of transparently allowing sync functions to be converted to async functions without fundamentally affecting consumer calling code.

For those who want to be able to do extensive async programming, having this in the language, and using it instead of `await` `async` throughout, is a no-brainer.

Of course, I am qualifying that it must be new keywords, not `await` `async` juggled like in the original post, but that wasn't the thrust of the proposal anyway.

On Mon, 4 Dec 2017 at 21:09 Isiah Meadows <[hidden email]> wrote:
Am I misunderstanding something about this proposal that it's substantially any different from `.then` or immediately invoked async functions?

```js
// Original
await function foo() {
    const bar = async baz()
    use(bar)
}

// What I'm reading
function foo() {
    ;(async () => {
        const bar = await baz()
        use(bar)
    })()
}
function foo() {
    try {
        Promise.resolve(baz())
        .then(bar => { use(bar) })
    } catch (e) {
        Promise.reject(e)
    }
}
```


On Mon, Dec 4, 2017, 09:54 T.J. Crowder <[hidden email]> wrote:
On Mon, Dec 4, 2017 at 2:37 PM, Bob Myers <[hidden email]> wrote:
>
> It turns out that this approach has a number of problems.
> As a result, future versions of selenium will no longer support it.
> Test writers will be asked to write `await` where needed.

Were there problems other than the complexity of maintaining the
promise manager tool and issues with debuggability? Neither of those
seems like it would be an issue with Steven's proposal...

(I just realized for the first time, Bob, that your initials are RTM.
I love it. You should adopt Frank as a second middle name. ;-) )

-- T.J. Crowder
_______________________________________________
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

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

Re: async/await -> await/async: a simpler, less error-prone async syntax

kai zhu
you can avoid this entire debate by using trusty and simple callbacks instead.  here's real-world example of using a callback like you would async/await, to asynchronously test various states/scenarios for user-login in an app (using recursion to avoid callback-hell):


```js
local.testCase_userLoginXxx_default = function (options, onError) {
/*
 * this function will test userLoginXxx's default handling-behavior
 */
    var modeNext, onNext;
    modeNext = 0;
    onNext = function (error, data) {
        modeNext += 1;
        switch (modeNext) {
        case 1:
            // cleanup userJwtEncrypted
            delete local.userJwtEncrypted;
            // test userLogout's default handling-behavior
            options = {};
            local.userLogout(options, onNext);
            break;
        case 2:
            // validate error occurred
            local.assert(error, error);
            // test userLoginByPassword's 401 handling-behavior
            options = { password: 'undefined', username: 'undefined' };
            local.userLoginByPassword(options, onNext);
            break;
        case 3:
            // validate error occurred
            local.assert(error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 401);
            // validate userJwtEncrypted does not exist
            local.assert(!local.userJwtEncrypted, local.userJwtEncrypted);
            // test userLogout's 401 handling-behavior
            options = {};
            local.userLogout(options, onNext);
            break;
        case 4:
            // validate error occurred
            local.assert(error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 401);
            // validate userJwtEncrypted does not exist
            local.assert(!local.userJwtEncrypted, local.userJwtEncrypted);
            // test userLoginByPassword's 200 handling-behavior
            options = { password: 'secret', username: 'admin' };
            local.userLoginByPassword(options, onNext);
            break;
        case 5:
            // validate no error occurred
            local.assert(!error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 200);
            // validate userJwtEncrypted exists
            local.assert(local.userJwtEncrypted, local.userJwtEncrypted);
            // test persistent-session handling-behavior
            local.apiDict['x-test crudNullGet']._ajax({}, onNext);
            break;
        case 6:
            // validate no error occurred
            local.assert(!error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 200);
            // validate userJwtEncrypted exists
            local.assert(local.userJwtEncrypted, local.userJwtEncrypted);
            // test userLogout's 200 handling-behavior
            // test jwtEncoded's update handling-behavior
            options = { jwtEncrypted: local.jwtA256GcmEncrypt({ sub: 'admin' }) };
            local.userLogout(options, onNext);
            break;
        case 7:
            // validate no error occurred
            local.assert(!error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 200);
            // validate userJwtEncrypted exists
            local.assert(local.userJwtEncrypted, local.userJwtEncrypted);
            // test userLogout's 401 handling-behavior
            options = {};
            local.userLogout(options, onNext);
            break;
        case 8:
            // validate error occurred
            local.assert(error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 401);
            // test userLoginByPassword's 400 handling-behavior
            local.ajax({ url: '/api/v0/user/userLoginByPassword?password=1' }, onNext);
            break;
        case 9:
            // validate error occurred
            local.assert(error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 400);
            // test userLogout's invalid-username handling-behavior
            options = { jwtEncrypted: local.jwtA256GcmEncrypt({ sub: 'undefined' }) };
            local.userLogout(options, onNext);
            break;
        case 10:
            // validate error occurred
            local.assert(error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 401);
            onError(null, data);
            break;
        }
    };
    onNext();
};
```

and here’s screenshot of setting a single-breakpoint to easily debug and step through all 10 asynchronous switch-statements in the callback.

-kai



On Dec 6, 2017, at 6:38 AM, Alexander Jones <[hidden email]> wrote:

with the added bonus of transparently allowing sync functions to be converted to async functions without fundamentally affecting consumer calling code.

It does fundamentally affect calling code if the calling code reaches outside of its local variables. The state of the world around you might change any time you await and give control back to the event loop.

On Mon, 4 Dec 2017 at 19:16, Naveen Chawla <[hidden email]> wrote:
Obviously. The whole point of this proposal is that awaiting async functions is automatically _implied_ inside an `autoasync` function, unless an async function is called with a `background` qualifier (which thereby makes it return its promise instead). The OP is right: this is a far less bug prone way to do async programming than `async` `await`, while offering all its functionality, with the added bonus of transparently allowing sync functions to be converted to async functions without fundamentally affecting consumer calling code.

For those who want to be able to do extensive async programming, having this in the language, and using it instead of `await` `async` throughout, is a no-brainer.

Of course, I am qualifying that it must be new keywords, not `await` `async` juggled like in the original post, but that wasn't the thrust of the proposal anyway.

On Mon, 4 Dec 2017 at 21:09 Isiah Meadows <[hidden email]> wrote:
Am I misunderstanding something about this proposal that it's substantially any different from `.then` or immediately invoked async functions?

```js
// Original
await function foo() {
    const bar = async baz()
    use(bar)
}

// What I'm reading
function foo() {
    ;(async () => {
        const bar = await baz()
        use(bar)
    })()
}
function foo() {
    try {
        Promise.resolve(baz())
        .then(bar => { use(bar) })
    } catch (e) {
        Promise.reject(e)
    }
}
```


On Mon, Dec 4, 2017, 09:54 T.J. Crowder <[hidden email]> wrote:
On Mon, Dec 4, 2017 at 2:37 PM, Bob Myers <[hidden email]> wrote:
>
> It turns out that this approach has a number of problems.
> As a result, future versions of selenium will no longer support it.
> Test writers will be asked to write `await` where needed.

Were there problems other than the complexity of maintaining the
promise manager tool and issues with debuggability? Neither of those
seems like it would be an issue with Steven's proposal...

(I just realized for the first time, Bob, that your initials are RTM.
I love it. You should adopt Frank as a second middle name. ;-) )

-- T.J. Crowder
_______________________________________________
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
_______________________________________________
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: async/await -> await/async: a simpler, less error-prone async syntax

Naveen Chawla
kai, no, that gets less and less manageable as the complexity of the async flow increases

On Wed, 6 Dec 2017 at 07:19 kai zhu <[hidden email]> wrote:
you can avoid this entire debate by using trusty and simple callbacks instead.  here's real-world example of using a callback like you would async/await, to asynchronously test various states/scenarios for user-login in an app (using recursion to avoid callback-hell):


```js
local.testCase_userLoginXxx_default = function (options, onError) {
/*
 * this function will test userLoginXxx's default handling-behavior
 */
    var modeNext, onNext;
    modeNext = 0;
    onNext = function (error, data) {
        modeNext += 1;
        switch (modeNext) {
        case 1:
            // cleanup userJwtEncrypted
            delete local.userJwtEncrypted;
            // test userLogout's default handling-behavior
            options = {};
            local.userLogout(options, onNext);
            break;
        case 2:
            // validate error occurred
            local.assert(error, error);
            // test userLoginByPassword's 401 handling-behavior
            options = { password: 'undefined', username: 'undefined' };
            local.userLoginByPassword(options, onNext);
            break;
        case 3:
            // validate error occurred
            local.assert(error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 401);
            // validate userJwtEncrypted does not exist
            local.assert(!local.userJwtEncrypted, local.userJwtEncrypted);
            // test userLogout's 401 handling-behavior
            options = {};
            local.userLogout(options, onNext);
            break;
        case 4:
            // validate error occurred
            local.assert(error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 401);
            // validate userJwtEncrypted does not exist
            local.assert(!local.userJwtEncrypted, local.userJwtEncrypted);
            // test userLoginByPassword's 200 handling-behavior
            options = { password: 'secret', username: 'admin' };
            local.userLoginByPassword(options, onNext);
            break;
        case 5:
            // validate no error occurred
            local.assert(!error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 200);
            // validate userJwtEncrypted exists
            local.assert(local.userJwtEncrypted, local.userJwtEncrypted);
            // test persistent-session handling-behavior
            local.apiDict['x-test crudNullGet']._ajax({}, onNext);
            break;
        case 6:
            // validate no error occurred
            local.assert(!error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 200);
            // validate userJwtEncrypted exists
            local.assert(local.userJwtEncrypted, local.userJwtEncrypted);
            // test userLogout's 200 handling-behavior
            // test jwtEncoded's update handling-behavior
            options = { jwtEncrypted: local.jwtA256GcmEncrypt({ sub: 'admin' }) };
            local.userLogout(options, onNext);
            break;
        case 7:
            // validate no error occurred
            local.assert(!error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 200);
            // validate userJwtEncrypted exists
            local.assert(local.userJwtEncrypted, local.userJwtEncrypted);
            // test userLogout's 401 handling-behavior
            options = {};
            local.userLogout(options, onNext);
            break;
        case 8:
            // validate error occurred
            local.assert(error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 401);
            // test userLoginByPassword's 400 handling-behavior
            local.ajax({ url: '/api/v0/user/userLoginByPassword?password=1' }, onNext);
            break;
        case 9:
            // validate error occurred
            local.assert(error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 400);
            // test userLogout's invalid-username handling-behavior
            options = { jwtEncrypted: local.jwtA256GcmEncrypt({ sub: 'undefined' }) };
            local.userLogout(options, onNext);
            break;
        case 10:
            // validate error occurred
            local.assert(error, error);
            // validate statusCode
            local.assertJsonEqual(data.statusCode, 401);
            onError(null, data);
            break;
        }
    };
    onNext();
};
```

and here’s screenshot of setting a single-breakpoint to easily debug and step through all 10 asynchronous switch-statements in the callback.

-kai



On Dec 6, 2017, at 6:38 AM, Alexander Jones <[hidden email]> wrote:

with the added bonus of transparently allowing sync functions to be converted to async functions without fundamentally affecting consumer calling code.

It does fundamentally affect calling code if the calling code reaches outside of its local variables. The state of the world around you might change any time you await and give control back to the event loop.

On Mon, 4 Dec 2017 at 19:16, Naveen Chawla <[hidden email]> wrote:
Obviously. The whole point of this proposal is that awaiting async functions is automatically _implied_ inside an `autoasync` function, unless an async function is called with a `background` qualifier (which thereby makes it return its promise instead). The OP is right: this is a far less bug prone way to do async programming than `async` `await`, while offering all its functionality, with the added bonus of transparently allowing sync functions to be converted to async functions without fundamentally affecting consumer calling code.

For those who want to be able to do extensive async programming, having this in the language, and using it instead of `await` `async` throughout, is a no-brainer.

Of course, I am qualifying that it must be new keywords, not `await` `async` juggled like in the original post, but that wasn't the thrust of the proposal anyway.

On Mon, 4 Dec 2017 at 21:09 Isiah Meadows <[hidden email]> wrote:
Am I misunderstanding something about this proposal that it's substantially any different from `.then` or immediately invoked async functions?

```js
// Original
await function foo() {
    const bar = async baz()
    use(bar)
}

// What I'm reading
function foo() {
    ;(async () => {
        const bar = await baz()
        use(bar)
    })()
}
function foo() {
    try {
        Promise.resolve(baz())
        .then(bar => { use(bar) })
    } catch (e) {
        Promise.reject(e)
    }
}
```


On Mon, Dec 4, 2017, 09:54 T.J. Crowder <[hidden email]> wrote:
On Mon, Dec 4, 2017 at 2:37 PM, Bob Myers <[hidden email]> wrote:
>
> It turns out that this approach has a number of problems.
> As a result, future versions of selenium will no longer support it.
> Test writers will be asked to write `await` where needed.

Were there problems other than the complexity of maintaining the
promise manager tool and issues with debuggability? Neither of those
seems like it would be an issue with Steven's proposal...

(I just realized for the first time, Bob, that your initials are RTM.
I love it. You should adopt Frank as a second middle name. ;-) )

-- T.J. Crowder
_______________________________________________
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
_______________________________________________
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

Screen Shot 2017-12-06 at 8.35.29 AM.png (2M) Download Attachment
Screen Shot 2017-12-06 at 8.35.29 AM.png (2M) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: async/await -> await/async: a simpler, less error-prone async syntax

Isiah Meadows-2
In reply to this post by kai zhu
Kai, please read [this article][1]. Also, please stop filling your
emails with walls of code - it makes it *much* easier to comprehend.
If you feel the need to post pictures, please find an image hosting
service or something. If you feel the need to show large amounts of
code, please try using GH Gists, Pastebin, or similar. It just gets in
the way of reading the emails, and not everyone can even see the
images (notably if they aren't using an HTML-supporting client).

[1]: https://medium.freecodecamp.org/javascript-from-callbacks-to-async-await-1cc090ddad99?gi=170f198491b1
-----

Isiah Meadows
[hidden email]

Looking for web consulting? Or a new website?
Send me an email and we can get started.
www.isiahmeadows.com


On Tue, Dec 5, 2017 at 8:49 PM, kai zhu <[hidden email]> wrote:

> you can avoid this entire debate by using trusty and simple callbacks
> instead.  here's real-world example of using a callback like you would
> async/await, to asynchronously test various states/scenarios for user-login
> in an app (using recursion to avoid callback-hell):
>
> https://github.com/kaizhu256/node-swgg/blob/2017.7.24/test.js#L918
>
> ```js
> local.testCase_userLoginXxx_default = function (options, onError) {
> /*
>  * this function will test userLoginXxx's default handling-behavior
>  */
>     var modeNext, onNext;
>     modeNext = 0;
>     onNext = function (error, data) {
>         modeNext += 1;
>         switch (modeNext) {
>         case 1:
>             // cleanup userJwtEncrypted
>             delete local.userJwtEncrypted;
>             // test userLogout's default handling-behavior
>             options = {};
>             local.userLogout(options, onNext);
>             break;
>         case 2:
>             // validate error occurred
>             local.assert(error, error);
>             // test userLoginByPassword's 401 handling-behavior
>             options = { password: 'undefined', username: 'undefined' };
>             local.userLoginByPassword(options, onNext);
>             break;
>         case 3:
>             // validate error occurred
>             local.assert(error, error);
>             // validate statusCode
>             local.assertJsonEqual(data.statusCode, 401);
>             // validate userJwtEncrypted does not exist
>             local.assert(!local.userJwtEncrypted, local.userJwtEncrypted);
>             // test userLogout's 401 handling-behavior
>             options = {};
>             local.userLogout(options, onNext);
>             break;
>         case 4:
>             // validate error occurred
>             local.assert(error, error);
>             // validate statusCode
>             local.assertJsonEqual(data.statusCode, 401);
>             // validate userJwtEncrypted does not exist
>             local.assert(!local.userJwtEncrypted, local.userJwtEncrypted);
>             // test userLoginByPassword's 200 handling-behavior
>             options = { password: 'secret', username: 'admin' };
>             local.userLoginByPassword(options, onNext);
>             break;
>         case 5:
>             // validate no error occurred
>             local.assert(!error, error);
>             // validate statusCode
>             local.assertJsonEqual(data.statusCode, 200);
>             // validate userJwtEncrypted exists
>             local.assert(local.userJwtEncrypted, local.userJwtEncrypted);
>             // test persistent-session handling-behavior
>             local.apiDict['x-test crudNullGet']._ajax({}, onNext);
>             break;
>         case 6:
>             // validate no error occurred
>             local.assert(!error, error);
>             // validate statusCode
>             local.assertJsonEqual(data.statusCode, 200);
>             // validate userJwtEncrypted exists
>             local.assert(local.userJwtEncrypted, local.userJwtEncrypted);
>             // test userLogout's 200 handling-behavior
>             // test jwtEncoded's update handling-behavior
>             options = { jwtEncrypted: local.jwtA256GcmEncrypt({ sub: 'admin'
> }) };
>             local.userLogout(options, onNext);
>             break;
>         case 7:
>             // validate no error occurred
>             local.assert(!error, error);
>             // validate statusCode
>             local.assertJsonEqual(data.statusCode, 200);
>             // validate userJwtEncrypted exists
>             local.assert(local.userJwtEncrypted, local.userJwtEncrypted);
>             // test userLogout's 401 handling-behavior
>             options = {};
>             local.userLogout(options, onNext);
>             break;
>         case 8:
>             // validate error occurred
>             local.assert(error, error);
>             // validate statusCode
>             local.assertJsonEqual(data.statusCode, 401);
>             // test userLoginByPassword's 400 handling-behavior
>             local.ajax({ url: '/api/v0/user/userLoginByPassword?password=1'
> }, onNext);
>             break;
>         case 9:
>             // validate error occurred
>             local.assert(error, error);
>             // validate statusCode
>             local.assertJsonEqual(data.statusCode, 400);
>             // test userLogout's invalid-username handling-behavior
>             options = { jwtEncrypted: local.jwtA256GcmEncrypt({ sub:
> 'undefined' }) };
>             local.userLogout(options, onNext);
>             break;
>         case 10:
>             // validate error occurred
>             local.assert(error, error);
>             // validate statusCode
>             local.assertJsonEqual(data.statusCode, 401);
>             onError(null, data);
>             break;
>         }
>     };
>     onNext();
> };
> ```
>
> and here’s screenshot of setting a single-breakpoint to easily debug and
> step through all 10 asynchronous switch-statements in the callback.
> you can reproduce the test-case in screenshot with this link:
> https://kaizhu256.github.io/node-swgg/build..beta..travis-ci.org/app/?modeTest=1&modeTestCase=testCase_userLoginXxx_default
>
> -kai
>
>
>
> On Dec 6, 2017, at 6:38 AM, Alexander Jones <[hidden email]> wrote:
>
>> with the added bonus of transparently allowing sync functions to be
>> converted to async functions without fundamentally affecting consumer
>> calling code.
>
> It does fundamentally affect calling code if the calling code reaches
> outside of its local variables. The state of the world around you might
> change any time you await and give control back to the event loop.
>
> On Mon, 4 Dec 2017 at 19:16, Naveen Chawla <[hidden email]> wrote:
>>
>> Obviously. The whole point of this proposal is that awaiting async
>> functions is automatically _implied_ inside an `autoasync` function, unless
>> an async function is called with a `background` qualifier (which thereby
>> makes it return its promise instead). The OP is right: this is a far less
>> bug prone way to do async programming than `async` `await`, while offering
>> all its functionality, with the added bonus of transparently allowing sync
>> functions to be converted to async functions without fundamentally affecting
>> consumer calling code.
>>
>> For those who want to be able to do extensive async programming, having
>> this in the language, and using it instead of `await` `async` throughout, is
>> a no-brainer.
>>
>> Of course, I am qualifying that it must be new keywords, not `await`
>> `async` juggled like in the original post, but that wasn't the thrust of the
>> proposal anyway.
>>
>> On Mon, 4 Dec 2017 at 21:09 Isiah Meadows <[hidden email]> wrote:
>>>
>>> Am I misunderstanding something about this proposal that it's
>>> substantially any different from `.then` or immediately invoked async
>>> functions?
>>>
>>> ```js
>>> // Original
>>> await function foo() {
>>>     const bar = async baz()
>>>     use(bar)
>>> }
>>>
>>> // What I'm reading
>>> function foo() {
>>>     ;(async () => {
>>>         const bar = await baz()
>>>         use(bar)
>>>     })()
>>> }
>>> function foo() {
>>>     try {
>>>         Promise.resolve(baz())
>>>         .then(bar => { use(bar) })
>>>     } catch (e) {
>>>         Promise.reject(e)
>>>     }
>>> }
>>> ```
>>>
>>>
>>> On Mon, Dec 4, 2017, 09:54 T.J. Crowder <[hidden email]>
>>> wrote:
>>>>
>>>> On Mon, Dec 4, 2017 at 2:37 PM, Bob Myers <[hidden email]> wrote:
>>>> >
>>>> > It turns out that this approach has a number of problems.
>>>> > As a result, future versions of selenium will no longer support it.
>>>> > Test writers will be asked to write `await` where needed.
>>>>
>>>> Were there problems other than the complexity of maintaining the
>>>> promise manager tool and issues with debuggability? Neither of those
>>>> seems like it would be an issue with Steven's proposal...
>>>>
>>>> (I just realized for the first time, Bob, that your initials are RTM.
>>>> I love it. You should adopt Frank as a second middle name. ;-) )
>>>>
>>>> -- T.J. Crowder
>>>> _______________________________________________
>>>> 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
>
> _______________________________________________
> 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
Reply | Threaded
Open this post in threaded view
|

Re: async/await -> await/async: a simpler, less error-prone async syntax

kai zhu
@isiah thx for link and read the article (i actually enjoy reading most of the links you post on this mailing list).

we can agree to disagree - i've been using recursive-callbacks for 5 years now. they’re quite powerful and elegantly solve the callback-hell issue.  recursive-callbacks can do pretty much anything you can do with async-await / generators / promises, are very easy to debug with breakpoints, and most importantly have no unobservable magic going on behind-the-scene.

> On Dec 7, 2017, at 2:35 AM, Isiah Meadows <[hidden email]> wrote:
>
> Kai, please read [this article][1]. Also, please stop filling your
> emails with walls of code - it makes it *much* easier to comprehend.
> If you feel the need to post pictures, please find an image hosting
> service or something. If you feel the need to show large amounts of
> code, please try using GH Gists, Pastebin, or similar. It just gets in
> the way of reading the emails, and not everyone can even see the
> images (notably if they aren't using an HTML-supporting client).
>
> [1]: https://medium.freecodecamp.org/javascript-from-callbacks-to-async-await-1cc090ddad99?gi=170f198491b1
> -----
>
> Isiah Meadows
> [hidden email]
>
> Looking for web consulting? Or a new website?
> Send me an email and we can get started.
> www.isiahmeadows.com
>
>
> On Tue, Dec 5, 2017 at 8:49 PM, kai zhu <[hidden email]> wrote:
>> you can avoid this entire debate by using trusty and simple callbacks
>> instead.  here's real-world example of using a callback like you would
>> async/await, to asynchronously test various states/scenarios for user-login
>> in an app (using recursion to avoid callback-hell):
>>
>> https://github.com/kaizhu256/node-swgg/blob/2017.7.24/test.js#L918
>>
>> ```js
>> local.testCase_userLoginXxx_default = function (options, onError) {
>> /*
>> * this function will test userLoginXxx's default handling-behavior
>> */
>>    var modeNext, onNext;
>>    modeNext = 0;
>>    onNext = function (error, data) {
>>        modeNext += 1;
>>        switch (modeNext) {
>>        case 1:
>>            // cleanup userJwtEncrypted
>>            delete local.userJwtEncrypted;
>>            // test userLogout's default handling-behavior
>>            options = {};
>>            local.userLogout(options, onNext);
>>            break;
>>        case 2:
>>            // validate error occurred
>>            local.assert(error, error);
>>            // test userLoginByPassword's 401 handling-behavior
>>            options = { password: 'undefined', username: 'undefined' };
>>            local.userLoginByPassword(options, onNext);
>>            break;
>>        case 3:
>>            // validate error occurred
>>            local.assert(error, error);
>>            // validate statusCode
>>            local.assertJsonEqual(data.statusCode, 401);
>>            // validate userJwtEncrypted does not exist
>>            local.assert(!local.userJwtEncrypted, local.userJwtEncrypted);
>>            // test userLogout's 401 handling-behavior
>>            options = {};
>>            local.userLogout(options, onNext);
>>            break;
>>        case 4:
>>            // validate error occurred
>>            local.assert(error, error);
>>            // validate statusCode
>>            local.assertJsonEqual(data.statusCode, 401);
>>            // validate userJwtEncrypted does not exist
>>            local.assert(!local.userJwtEncrypted, local.userJwtEncrypted);
>>            // test userLoginByPassword's 200 handling-behavior
>>            options = { password: 'secret', username: 'admin' };
>>            local.userLoginByPassword(options, onNext);
>>            break;
>>        case 5:
>>            // validate no error occurred
>>            local.assert(!error, error);
>>            // validate statusCode
>>            local.assertJsonEqual(data.statusCode, 200);
>>            // validate userJwtEncrypted exists
>>            local.assert(local.userJwtEncrypted, local.userJwtEncrypted);
>>            // test persistent-session handling-behavior
>>            local.apiDict['x-test crudNullGet']._ajax({}, onNext);
>>            break;
>>        case 6:
>>            // validate no error occurred
>>            local.assert(!error, error);
>>            // validate statusCode
>>            local.assertJsonEqual(data.statusCode, 200);
>>            // validate userJwtEncrypted exists
>>            local.assert(local.userJwtEncrypted, local.userJwtEncrypted);
>>            // test userLogout's 200 handling-behavior
>>            // test jwtEncoded's update handling-behavior
>>            options = { jwtEncrypted: local.jwtA256GcmEncrypt({ sub: 'admin'
>> }) };
>>            local.userLogout(options, onNext);
>>            break;
>>        case 7:
>>            // validate no error occurred
>>            local.assert(!error, error);
>>            // validate statusCode
>>            local.assertJsonEqual(data.statusCode, 200);
>>            // validate userJwtEncrypted exists
>>            local.assert(local.userJwtEncrypted, local.userJwtEncrypted);
>>            // test userLogout's 401 handling-behavior
>>            options = {};
>>            local.userLogout(options, onNext);
>>            break;
>>        case 8:
>>            // validate error occurred
>>            local.assert(error, error);
>>            // validate statusCode
>>            local.assertJsonEqual(data.statusCode, 401);
>>            // test userLoginByPassword's 400 handling-behavior
>>            local.ajax({ url: '/api/v0/user/userLoginByPassword?password=1'
>> }, onNext);
>>            break;
>>        case 9:
>>            // validate error occurred
>>            local.assert(error, error);
>>            // validate statusCode
>>            local.assertJsonEqual(data.statusCode, 400);
>>            // test userLogout's invalid-username handling-behavior
>>            options = { jwtEncrypted: local.jwtA256GcmEncrypt({ sub:
>> 'undefined' }) };
>>            local.userLogout(options, onNext);
>>            break;
>>        case 10:
>>            // validate error occurred
>>            local.assert(error, error);
>>            // validate statusCode
>>            local.assertJsonEqual(data.statusCode, 401);
>>            onError(null, data);
>>            break;
>>        }
>>    };
>>    onNext();
>> };
>> ```
>>
>> and here’s screenshot of setting a single-breakpoint to easily debug and
>> step through all 10 asynchronous switch-statements in the callback.
>> you can reproduce the test-case in screenshot with this link:
>> https://kaizhu256.github.io/node-swgg/build..beta..travis-ci.org/app/?modeTest=1&modeTestCase=testCase_userLoginXxx_default
>>
>> -kai
>>
>>
>>
>> On Dec 6, 2017, at 6:38 AM, Alexander Jones <[hidden email]> wrote:
>>
>>> with the added bonus of transparently allowing sync functions to be
>>> converted to async functions without fundamentally affecting consumer
>>> calling code.
>>
>> It does fundamentally affect calling code if the calling code reaches
>> outside of its local variables. The state of the world around you might
>> change any time you await and give control back to the event loop.
>>
>> On Mon, 4 Dec 2017 at 19:16, Naveen Chawla <[hidden email]> wrote:
>>>
>>> Obviously. The whole point of this proposal is that awaiting async
>>> functions is automatically _implied_ inside an `autoasync` function, unless
>>> an async function is called with a `background` qualifier (which thereby
>>> makes it return its promise instead). The OP is right: this is a far less
>>> bug prone way to do async programming than `async` `await`, while offering
>>> all its functionality, with the added bonus of transparently allowing sync
>>> functions to be converted to async functions without fundamentally affecting
>>> consumer calling code.
>>>
>>> For those who want to be able to do extensive async programming, having
>>> this in the language, and using it instead of `await` `async` throughout, is
>>> a no-brainer.
>>>
>>> Of course, I am qualifying that it must be new keywords, not `await`
>>> `async` juggled like in the original post, but that wasn't the thrust of the
>>> proposal anyway.
>>>
>>> On Mon, 4 Dec 2017 at 21:09 Isiah Meadows <[hidden email]> wrote:
>>>>
>>>> Am I misunderstanding something about this proposal that it's
>>>> substantially any different from `.then` or immediately invoked async
>>>> functions?
>>>>
>>>> ```js
>>>> // Original
>>>> await function foo() {
>>>>    const bar = async baz()
>>>>    use(bar)
>>>> }
>>>>
>>>> // What I'm reading
>>>> function foo() {
>>>>    ;(async () => {
>>>>        const bar = await baz()
>>>>        use(bar)
>>>>    })()
>>>> }
>>>> function foo() {
>>>>    try {
>>>>        Promise.resolve(baz())
>>>>        .then(bar => { use(bar) })
>>>>    } catch (e) {
>>>>        Promise.reject(e)
>>>>    }
>>>> }
>>>> ```
>>>>
>>>>
>>>> On Mon, Dec 4, 2017, 09:54 T.J. Crowder <[hidden email]>
>>>> wrote:
>>>>>
>>>>> On Mon, Dec 4, 2017 at 2:37 PM, Bob Myers <[hidden email]> wrote:
>>>>>>
>>>>>> It turns out that this approach has a number of problems.
>>>>>> As a result, future versions of selenium will no longer support it.
>>>>>> Test writers will be asked to write `await` where needed.
>>>>>
>>>>> Were there problems other than the complexity of maintaining the
>>>>> promise manager tool and issues with debuggability? Neither of those
>>>>> seems like it would be an issue with Steven's proposal...
>>>>>
>>>>> (I just realized for the first time, Bob, that your initials are RTM.
>>>>> I love it. You should adopt Frank as a second middle name. ;-) )
>>>>>
>>>>> -- T.J. Crowder
>>>>> _______________________________________________
>>>>> 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
>>
>> _______________________________________________
>> 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
Reply | Threaded
Open this post in threaded view
|

Re: async/await -> await/async: a simpler, less error-prone async syntax

Florian Bösch
In reply to this post by voracity
as I predicted once you use it, await/async infests all calls/funcdefs, and has now become pointless line-noise that you need to remember to write or you will get into trouble, but serves no discernable semantic, syntactic or logical function other than making you type more.

solution: write a transpiler that inserts await/async into all calls/funcdefs

On Sun, 3 Dec 2017 at 12:01, Steven Mascaro <[hidden email]> wrote:
Sorry for making this request now (rather than when async/await was first being formulated), but real-world use has led me to think some things could benefit from a slightly different syntax. I also apologise if this has been raised/discussed before, I wasn't able to find it. The proposal is most quickly explained with an example.

Current:

class RemoteService {
    async init() { ... }
    async setProp(id, val) { ...; return this }
    async getProp(id) { return ... }
    async runInBackground() { ... }
}

async function remoteExample() {
    let remote = new RemoteService();
    await remote.init();
    await (await remote.setProp('a', 1)).setProp('b', 2);
    remote.runInBackground();
    let val = await remote.getProp('a');
    return val;
}

Proposed:

class RemoteService {
    await init() { ... }
    await setProp(id, val) { ...; return this }
    await getProp(id) { return ... }
    await runInBackground() { ... }
}

await function remoteExample() {
    let remote = new RemoteService();
    remote.init();
    remote.setProp('a', 1).setProp('b', 2);
    async remote.runInBackground();
    let val = remote.getProp('a');
    return val;
}

Why:

Running things in a genuinely asynchronous way is actually *unusual*. The current async/await syntax (which I think should still stay) is the exact reverse of what fits the probable use cases. Missing the 'await' keyword (particularly on functions/methods that don't return a value) causes all sorts of hard to track down bugs. I myself didn't realise this until I (and others I work with) started making intense use of async/await.

The new proposed syntax above is obviously much simpler and would be very hard to get wrong. By contrast, the original syntax has proven surprisingly difficult to get consistently right. Even now with quite a lot of practice, I'm occasionally forgetting an await here and there. In addition, patterns like chaining get ugly *very* quickly. (Although I guess that could also be fixed with a method-friendly async call syntax.)

If the proposed syntax were available in addition to the current syntax, you could apply whichever keyword in the function declaration is most likely applicable at call sites. e.g. runInBackground could be declared 'async' rather than 'await', if you normally expect it to be run in the background.

_______________________________________________
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: async/await -> await/async: a simpler, less error-prone async syntax

Isiah Meadows-2
The real solution to that would be stackful coroutines, but those are tricky to implement and are substantially slower at lower nesting levels. (Go's goroutines are built from this under the hood.) There's tradeoffs to be made.

On Thu, Dec 7, 2017, 05:45 Florian Bösch <[hidden email]> wrote:
as I predicted once you use it, await/async infests all calls/funcdefs, and has now become pointless line-noise that you need to remember to write or you will get into trouble, but serves no discernable semantic, syntactic or logical function other than making you type more.

solution: write a transpiler that inserts await/async into all calls/funcdefs

On Sun, 3 Dec 2017 at 12:01, Steven Mascaro <[hidden email]> wrote:
Sorry for making this request now (rather than when async/await was first being formulated), but real-world use has led me to think some things could benefit from a slightly different syntax. I also apologise if this has been raised/discussed before, I wasn't able to find it. The proposal is most quickly explained with an example.

Current:

class RemoteService {
    async init() { ... }
    async setProp(id, val) { ...; return this }
    async getProp(id) { return ... }
    async runInBackground() { ... }
}

async function remoteExample() {
    let remote = new RemoteService();
    await remote.init();
    await (await remote.setProp('a', 1)).setProp('b', 2);
    remote.runInBackground();
    let val = await remote.getProp('a');
    return val;
}

Proposed:

class RemoteService {
    await init() { ... }
    await setProp(id, val) { ...; return this }
    await getProp(id) { return ... }
    await runInBackground() { ... }
}

await function remoteExample() {
    let remote = new RemoteService();
    remote.init();
    remote.setProp('a', 1).setProp('b', 2);
    async remote.runInBackground();
    let val = remote.getProp('a');
    return val;
}

Why:

Running things in a genuinely asynchronous way is actually *unusual*. The current async/await syntax (which I think should still stay) is the exact reverse of what fits the probable use cases. Missing the 'await' keyword (particularly on functions/methods that don't return a value) causes all sorts of hard to track down bugs. I myself didn't realise this until I (and others I work with) started making intense use of async/await.

The new proposed syntax above is obviously much simpler and would be very hard to get wrong. By contrast, the original syntax has proven surprisingly difficult to get consistently right. Even now with quite a lot of practice, I'm occasionally forgetting an await here and there. In addition, patterns like chaining get ugly *very* quickly. (Although I guess that could also be fixed with a method-friendly async call syntax.)

If the proposed syntax were available in addition to the current syntax, you could apply whichever keyword in the function declaration is most likely applicable at call sites. e.g. runInBackground could be declared 'async' rather than 'await', if you normally expect it to be run in the background.

_______________________________________________
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
Reply | Threaded
Open this post in threaded view
|

Re: async/await -> await/async: a simpler, less error-prone async syntax

kai zhu

On Dec 7, 2017, at 5:50 PM, Isiah Meadows <[hidden email]> wrote:

The real solution to that would be stackful coroutines, but those are tricky to implement and are substantially slower at lower nesting levels. (Go's goroutines are built from this under the hood.) There's tradeoffs to be made.

that is never going to happen. unless the goal is to intentionally sabotage javascript’s fundamental-design and cause even greater chaos and instability in the world of frontend-development.


On Thu, Dec 7, 2017, 05:45 Florian Bösch <[hidden email]> wrote:
as I predicted once you use it, await/async infests all calls/funcdefs, and has now become pointless line-noise that you need to remember to write or you will get into trouble, but serves no discernable semantic, syntactic or logical function other than making you type more.

solution: write a transpiler that inserts await/async into all calls/funcdefs

On Sun, 3 Dec 2017 at 12:01, Steven Mascaro <[hidden email]> wrote:
Sorry for making this request now (rather than when async/await was first being formulated), but real-world use has led me to think some things could benefit from a slightly different syntax. I also apologise if this has been raised/discussed before, I wasn't able to find it. The proposal is most quickly explained with an example.

Current:

class RemoteService {
    async init() { ... }
    async setProp(id, val) { ...; return this }
    async getProp(id) { return ... }
    async runInBackground() { ... }
}

async function remoteExample() {
    let remote = new RemoteService();
    await remote.init();
    await (await remote.setProp('a', 1)).setProp('b', 2);
    remote.runInBackground();
    let val = await remote.getProp('a');
    return val;
}

Proposed:

class RemoteService {
    await init() { ... }
    await setProp(id, val) { ...; return this }
    await getProp(id) { return ... }
    await runInBackground() { ... }
}

await function remoteExample() {
    let remote = new RemoteService();
    remote.init();
    remote.setProp('a', 1).setProp('b', 2);
    async remote.runInBackground();
    let val = remote.getProp('a');
    return val;
}

Why:

Running things in a genuinely asynchronous way is actually *unusual*. The current async/await syntax (which I think should still stay) is the exact reverse of what fits the probable use cases. Missing the 'await' keyword (particularly on functions/methods that don't return a value) causes all sorts of hard to track down bugs. I myself didn't realise this until I (and others I work with) started making intense use of async/await.

The new proposed syntax above is obviously much simpler and would be very hard to get wrong. By contrast, the original syntax has proven surprisingly difficult to get consistently right. Even now with quite a lot of practice, I'm occasionally forgetting an await here and there. In addition, patterns like chaining get ugly *very* quickly. (Although I guess that could also be fixed with a method-friendly async call syntax.)

If the proposed syntax were available in addition to the current syntax, you could apply whichever keyword in the function declaration is most likely applicable at call sites. e.g. runInBackground could be declared 'async' rather than 'await', if you normally expect it to be run in the background.

_______________________________________________
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


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