T.J. Crowder (2017-02-24T07:01:33.000Z)
On Fri, Feb 24, 2017 at 5:18 AM, Šime Vidas <sime.vidas at gmail.com> wrote:

> To clarify, the idea is to declare and kick off all the concurrent tasks
> upfront (using local variables and the ‘lazy await’ keyword), and then just
> continue writing the rest of the code ‘as if all the promises are
> resolved’. The async function automagically pauses whenever needed, so it’s
> no longer necessary to insert await operators throughout the code.
>

Not to be a naysayer, but I'm not a big fan of hidden behavior. If I see
`sauce.determineCheese()`, the idea that the `.` is triggering a
behind-the-scenes `await` isn't attractive. (Granted, there are already
plenty of things it could be doing behind the scenes, with getters and
proxies; but at least they're not changing the temporal semantics of the
statement.) Domenic's version using current `async`/`await` syntax is nice
and clear (one might tweak the variable names a bit to differentiate
promises from resolved values, but...).

Separately, I think you're going to run into implementational complexity.
These automatic-`await` values are neither promises nor resolved values,
they're a new beast with hidden `await` behavior; call them "hidden
promises." Within an `async` function, most but not all [GetValue][1]
operations on the variables/properties containing these hidden promises
would need an "if this is a hidden promise, `await` it" guard: Any math
operation, any string operation, any object operation, any time an `async`
function passes the value into a non-`async` function, etc. Just about the
only exception would be assignment (well, most assignments; more below),
which would just copy the hidden promise. This becomes particularly
problematic when you think about what it means to have one of these within
a structure, like an array or object; what if we then pass its container to
a non-`async` function? Do we recursively search the container for
automatic-`await` values and `await` them before calling the non-`async`
function? In what order? Similarly, the return values of `async` functions
would need vetting, but (arguably) only if being returned to non-`async`
functions, which makes for some new layer between caller and callee or an
uncomfortable awareness in the `async` function of where its return value
is going. Handling all of that sounds like a lot of runtime cost to simply
hide `await` from ourselves. And updating all of this in existing engines
seems like a lot of work.

Then there's the question of which assignment operations would need to
trigger a hidden `await`. Presumably not `let x = y;` where `y` is a hidden
promise, that would largely defeat the purpose. But consider:

```js
let x;

function example() {
    a().then(() => { /* Do something */ });
}
async function a() {
    async const hiddenPromise = getPromise(); // Or whatever the syntax
would be...
    x = hiddenPromise;
    return await hiddenPromise;
}
example();
console.log(x); // What does this see?
```

Finally, there's the educational cost of explaining what triggers a hidden
`await` to new JavaScripters.

I can imagine a new inherently-async language with this sort of thing at
its core (probably without non-`async` functions at all, avoiding a lot of
the complexity above; instead making async-until-the-last-second the
default with an explicit "resolve"). It could well be quite interesting.

But for JavaScript, I think we're better off with the explicit `await`.

-- T.J.

[1]: http://www.ecma-international.org/ecma-262/7.0/index.html#sec-getvalue
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20170224/5341c138/attachment.html>
tj.crowder at farsightsoftware.com (2017-02-24T07:27:49.898Z)
On Fri, Feb 24, 2017 at 5:18 AM, Šime Vidas <sime.vidas at gmail.com> wrote:

> To clarify, the idea is to declare and kick off all the concurrent tasks
> upfront (using local variables and the ‘lazy await’ keyword), and then just
> continue writing the rest of the code ‘as if all the promises are
> resolved’. The async function automagically pauses whenever needed, so it’s
> no longer necessary to insert await operators throughout the code.
>

Not to be a naysayer, but I'm not a big fan of hidden behavior. If I see
`sauce.determineCheese()`, the idea that the `.` is triggering a
behind-the-scenes `await` isn't attractive. (Granted, there are already
plenty of things it could be doing behind the scenes, with getters and
proxies; but at least they're not changing the temporal semantics of the
statement.) Domenic's version using current `async`/`await` syntax is nice
and clear (one might tweak the variable names a bit to differentiate
promises from resolved values, but...).

Separately, I think you're going to run into implementational complexity.
These automatic-`await` values are neither promises nor resolved values,
they're a new beast with hidden `await` behavior; call them "hidden
promises." Within an `async` function, most but not all [GetValue][1]
operations on the variables/properties containing these hidden promises
would need an "if this is a hidden promise, `await` it" guard: Any math
operation, any string operation, any object operation, any time an `async`
function passes the value into a non-`async` function, etc. Just about the
only exception would be assignment (well, most assignments; more below),
which would just copy the hidden promise. This becomes particularly
problematic when you think about what it means to have one of these within
a structure, like an array or object; what if we then pass its container to
a non-`async` function? Do we recursively search the container for
automatic-`await` values and `await` them before calling the non-`async`
function? In what order? Similarly, the return values of `async` functions
would need vetting, but (arguably) only if being returned to non-`async`
functions, which makes for some new layer between caller and callee or an
uncomfortable awareness in the `async` function of where its return value
is going. Handling all of that sounds like a lot of runtime cost to simply
hide `await` from ourselves. And updating all of this in existing engines
seems like a lot of work.

Then there's the question of which assignment operations would need to
trigger a hidden `await`. Presumably not `let x = y;` where `y` is a hidden
promise, that would largely defeat the purpose. But consider:

```js
let x;

function example() {
    a().then(() => { /* Do something */ });
}
async function a() {
    async const hiddenPromise = getPromise(); // Or whatever the syntax would be...
    x = hiddenPromise;
    return await hiddenPromise;
}
example();
console.log(x); // What does this see?
```

Finally, there's the educational cost of explaining what triggers a hidden
`await` to new JavaScripters.

I can imagine a new inherently-async language with this sort of thing at
its core (probably without non-`async` functions at all, avoiding a lot of
the complexity above; instead making async-until-the-last-second the
default with an explicit "resolve"). It could well be quite interesting.

But for JavaScript, I think we're better off with the explicit `await`.

-- T.J.

[1]: http://www.ecma-international.org/ecma-262/7.0/index.html#sec