Re: Conditional await, anyone?
I haven't read the whole thread, but I also disliked the ugly conditional checking I had to do to get faster results. I agree that this would changed perceived code execution order (unexpectedly, a breaking change). I believe that a better solution would be to make it explicit in the function definition, so that it carries up into caller code: ```js async? function foo() { ... } ``` Now, the user of the function must be required to use `await?` and thus has to be forced to think that the `foo` function might possibly run async, or might not. Using `await` on an `async? function` would be a runtime error, to further prevent possible confusion and errors. Only with a construct like `async? function` could I see this becoming possible, so that there aren't any surprising breaking changes in downstream projects. On Tue, Oct 15, 2019 at 12:26 AM Andrea Giammarchi wrote: > > > You still seem to be misunderstanding what the execution order difference > > is about. > > If to stop this thread you need me to say I am confused about anything then > fine, "I am confused", but if you keep changing my examples to make your > point then this conversation goes nowhere, so I am officially out of this > thread. > > Best Regards. > > On Mon, Oct 14, 2019 at 10:41 PM Tab Atkins Jr. wrote: >> >> On Sat, Oct 12, 2019 at 7:19 AM Andrea Giammarchi >> wrote: >> > in order to work, `await` must be executed in an async scope/context >> > (either top level or within a closure). >> > >> > In such case, either somebody is awaiting the result of that `async` >> > execution, or the order doesn't matter. >> >> That's definitely not true. I gave you an explicit example where the >> order differs. That example code is realistic if you're using the >> async call for side-effects only (and thus don't care about the >> returned promise), or if you're storing the returned promise in a >> variable so you can pass it to one of the promise combinators later. >> In either of these cases the order of execution between the sync and >> async code can definitely matter. >> >> > The following two examples produce indeed the very same result: >> > >> > ```js >> > (async () => { return 1; })().then(console.log); >> > console.log(2); >> > >> > (async () => { return await 1; })().then(console.log); >> > console.log(2); >> > ``` >> >> In both of these cases, you're doing no additional work after the >> "maybe async" point. That is the exact part that moves in execution >> order between the two cases, so obviously you won't see any >> difference. Here's a slightly altered version that shows off the >> difference: >> >> ```js >> (async () => { 1; console.log("async"); return 3; })().then(console.log); >> console.log(2); >> >> (async () => { await 1; console.log("async"); return 3; >> })().then(console.log); >> console.log(2); >> ``` >> >> In the first you'll log "async", "2", "3". In the second you'll log >> "2", "async", "3". >> >> > As summary: the proposal was to help engines be faster when it's possible, >> > but devs are confused by the syntax, and maybeat the end there wouldn't be >> > as many benefits compared to the apparent confusion this proposal would >> > add. >> >> You still seem to be misunderstanding what the execution order >> difference is about. Nobody's confused about the syntax; it's clear >> enough. It just does bad, confusing things as you've presented it. >> >> As I said in earlier message, there *is* a way to eliminate the >> execution-order difference (making it so the only difference would be >> the number of microtasks when your function awaits *multiple* sync >> values), which I thought you'd come up with at some point, but I'm >> pretty sure it was just me misunderstanding what you'd said. >> >> ~TJ > > ___ > es-discuss mailing list > es-discuss@mozilla.org > https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Conditional await, anyone?
> You still seem to be misunderstanding what the execution order difference is about. If to stop this thread you need me to say I am confused about anything then fine, "I am confused", but if you keep changing my examples to make your point then this conversation goes nowhere, so I am officially out of this thread. Best Regards. On Mon, Oct 14, 2019 at 10:41 PM Tab Atkins Jr. wrote: > On Sat, Oct 12, 2019 at 7:19 AM Andrea Giammarchi > wrote: > > in order to work, `await` must be executed in an async scope/context > (either top level or within a closure). > > > > In such case, either somebody is awaiting the result of that `async` > execution, or the order doesn't matter. > > That's definitely not true. I gave you an explicit example where the > order differs. That example code is realistic if you're using the > async call for side-effects only (and thus don't care about the > returned promise), or if you're storing the returned promise in a > variable so you can pass it to one of the promise combinators later. > In either of these cases the order of execution between the sync and > async code can definitely matter. > > > The following two examples produce indeed the very same result: > > > > ```js > > (async () => { return 1; })().then(console.log); > > console.log(2); > > > > (async () => { return await 1; })().then(console.log); > > console.log(2); > > ``` > > In both of these cases, you're doing no additional work after the > "maybe async" point. That is the exact part that moves in execution > order between the two cases, so obviously you won't see any > difference. Here's a slightly altered version that shows off the > difference: > > ```js > (async () => { 1; console.log("async"); return 3; })().then(console.log); > console.log(2); > > (async () => { await 1; console.log("async"); return 3; > })().then(console.log); > console.log(2); > ``` > > In the first you'll log "async", "2", "3". In the second you'll log > "2", "async", "3". > > > As summary: the proposal was to help engines be faster when it's > possible, but devs are confused by the syntax, and maybeat the end there > wouldn't be as many benefits compared to the apparent confusion this > proposal would add. > > You still seem to be misunderstanding what the execution order > difference is about. Nobody's confused about the syntax; it's clear > enough. It just does bad, confusing things as you've presented it. > > As I said in earlier message, there *is* a way to eliminate the > execution-order difference (making it so the only difference would be > the number of microtasks when your function awaits *multiple* sync > values), which I thought you'd come up with at some point, but I'm > pretty sure it was just me misunderstanding what you'd said. > > ~TJ > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Conditional await, anyone?
On Sat, Oct 12, 2019 at 7:19 AM Andrea Giammarchi wrote: > in order to work, `await` must be executed in an async scope/context (either > top level or within a closure). > > In such case, either somebody is awaiting the result of that `async` > execution, or the order doesn't matter. That's definitely not true. I gave you an explicit example where the order differs. That example code is realistic if you're using the async call for side-effects only (and thus don't care about the returned promise), or if you're storing the returned promise in a variable so you can pass it to one of the promise combinators later. In either of these cases the order of execution between the sync and async code can definitely matter. > The following two examples produce indeed the very same result: > > ```js > (async () => { return 1; })().then(console.log); > console.log(2); > > (async () => { return await 1; })().then(console.log); > console.log(2); > ``` In both of these cases, you're doing no additional work after the "maybe async" point. That is the exact part that moves in execution order between the two cases, so obviously you won't see any difference. Here's a slightly altered version that shows off the difference: ```js (async () => { 1; console.log("async"); return 3; })().then(console.log); console.log(2); (async () => { await 1; console.log("async"); return 3; })().then(console.log); console.log(2); ``` In the first you'll log "async", "2", "3". In the second you'll log "2", "async", "3". > As summary: the proposal was to help engines be faster when it's possible, > but devs are confused by the syntax, and maybeat the end there wouldn't be as > many benefits compared to the apparent confusion this proposal would add. You still seem to be misunderstanding what the execution order difference is about. Nobody's confused about the syntax; it's clear enough. It just does bad, confusing things as you've presented it. As I said in earlier message, there *is* a way to eliminate the execution-order difference (making it so the only difference would be the number of microtasks when your function awaits *multiple* sync values), which I thought you'd come up with at some point, but I'm pretty sure it was just me misunderstanding what you'd said. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Conditional await, anyone?
in order to work, `await` must be executed in an async scope/context (either top level or within a closure). In such case, either somebody is awaiting the result of that `async` execution, or the order doesn't matter. The following two examples produce indeed the very same result: ```js (async () => { return 1; })().then(console.log); console.log(2); (async () => { return await 1; })().then(console.log); console.log(2); ``` Except the second one will be scheduled a micro task too far. Since nobody counts the amount of microtasks per async function execution, the result is practically the same, except the second example is always slower. Putting all together: ```js (async () => { return await 'third'; })().then(console.log); (async () => { return 'second'; })().then(console.log); console.log('first'); ``` If you `await` the return of an async function, you are consuming that microtask regardless, which is what `await?` here would like to avoid: do not create a micro task when it's not necessary. There's no footgun as the `await?` is an explicit intent from the developer, so if the developer knows what s/he's doing, can use `await?`, otherwise if the order of the microtask matters at all, can always just use `await`. As summary: the proposal was to help engines be faster when it's possible, but devs are confused by the syntax, and maybeat the end there wouldn't be as many benefits compared to the apparent confusion this proposal would add. I hope I've explained properly what was this about. Regards On Fri, Oct 11, 2019 at 10:43 PM Tab Atkins Jr. wrote: > On Fri, Oct 11, 2019 at 1:15 AM Andrea Giammarchi > wrote: > > Again, the `await?` is sugar for the following: > > > > ```js > > const value = await? callback(); > > > > // as sugar for > > let value = callback(); > > if ('then' in value) > > value = await value; > > ``` > > Okay, so that has the "you can't predict execution order any more" > problem. But that's not consistent with what you said in "otherwise > schedule a single microtask if none has been scheduled already, or > queue this result to the previous scheduled one", which implies a > different desugaring: > > ```js > let value = callback(); > if('then' in value || thisFunctionHasntAwaitedYet) > value = await value; > ``` > > *This* desugaring has a consistent execution order, and still meets > your goal of "don't add a bunch of microtask checkpoints for > synchronous values". > > Put another way, this `await?` is equivalent to `await` *if* you're > still in the "synchronously execute until you hit the first await" > phase of executing an async function; but it's equivalent to your > simpler desugaring ("await only if this is a thenable") after that. > > > but since I've stated already I have no interest anymore in this > proposal, we can also stop explaining to each others things we know already. > > I'm fine if you want to drop it, but we're not explaining things we > already know to each other. At least one of us is confused about > what's being proposed. And the altered desugaring I give up above at > least has a chance of happening; the only issue might be the > observability of how many microtasks get scheduled. If that's not a > problem, it might be possible to suggest this as an actual change to > how `await` works. > > ~TJ > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Conditional await, anyone?
On Fri, Oct 11, 2019 at 1:15 AM Andrea Giammarchi wrote: > Again, the `await?` is sugar for the following: > > ```js > const value = await? callback(); > > // as sugar for > let value = callback(); > if ('then' in value) > value = await value; > ``` Okay, so that has the "you can't predict execution order any more" problem. But that's not consistent with what you said in "otherwise schedule a single microtask if none has been scheduled already, or queue this result to the previous scheduled one", which implies a different desugaring: ```js let value = callback(); if('then' in value || thisFunctionHasntAwaitedYet) value = await value; ``` *This* desugaring has a consistent execution order, and still meets your goal of "don't add a bunch of microtask checkpoints for synchronous values". Put another way, this `await?` is equivalent to `await` *if* you're still in the "synchronously execute until you hit the first await" phase of executing an async function; but it's equivalent to your simpler desugaring ("await only if this is a thenable") after that. > but since I've stated already I have no interest anymore in this proposal, we > can also stop explaining to each others things we know already. I'm fine if you want to drop it, but we're not explaining things we already know to each other. At least one of us is confused about what's being proposed. And the altered desugaring I give up above at least has a chance of happening; the only issue might be the observability of how many microtasks get scheduled. If that's not a problem, it might be possible to suggest this as an actual change to how `await` works. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Conditional await, anyone?
Again, the `await?` is sugar for the following: ```js const value = await? callback(); // as sugar for let value = callback(); if ('then' in value) value = await value; ``` but since I've stated already I have no interest anymore in this proposal, we can also stop explaining to each others things we know already. Best Regards On Fri, Oct 11, 2019 at 1:32 AM Tab Atkins Jr. wrote: > On Wed, Oct 9, 2019 at 11:17 PM Andrea Giammarchi > wrote: > > > What's the order of the logs? > > > > Exactly the same, as the `await?` is inevitably inside an `async` > function which would grant a single microtask instead of N. > > I think you're misreading my example? Check this out: > http://software.hixie.ch/utilities/js/live-dom-viewer/saved/7271 > > ```html > > > function one() { > oneAsync(); > w("one A"); > } > async function oneAsync() { > await Promise.resolve(); > w("one B"); > } > > function two() { > twoAsync(); > w("two A"); > } > > async function twoAsync() { > // await? true; > w("two B"); > } > > one(); > two(); > > ``` > > This script logs: > > ``` > log: one A > log: two B > log: two A > log: one B > ``` > > A and B are logged in different order depends on whether there's an > `await` creating a microtask checkpoint or not. `await? true` won't > create a microtask checkpoint, so you'll get the two() behavior, > printing B then A. `await? Promise.resolve(true)` will create one, so > you'll get the one() behavior, printing A then B. > > > If `maybeAsync` returns twice non promises results, there is only one > microtask within the async `tasks` function, that would linearly collect > all non promises, so that above example could have 1, 2, max 4 microtasks, > instead of always 4 > > . > > To explain `await?` in steps: > > > > * is the awaited value a promise? schedule microtask > > * otherwise schedule a single microtask if none has been scheduled > already, or queue this result to the previous scheduled one > > > > This would grant same linear order and save time. > > Ah, your earlier posts didn't say that `await? nonPromise` *would* > schedule a microtask in some cases! That does change things. Hm, I > wonder if this is observably different from the current behavior? > > ~TJ > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Conditional await, anyone?
On Wed, Oct 9, 2019 at 11:17 PM Andrea Giammarchi wrote: > > What's the order of the logs? > > Exactly the same, as the `await?` is inevitably inside an `async` function > which would grant a single microtask instead of N. I think you're misreading my example? Check this out: http://software.hixie.ch/utilities/js/live-dom-viewer/saved/7271 ```html function one() { oneAsync(); w("one A"); } async function oneAsync() { await Promise.resolve(); w("one B"); } function two() { twoAsync(); w("two A"); } async function twoAsync() { // await? true; w("two B"); } one(); two(); ``` This script logs: ``` log: one A log: two B log: two A log: one B ``` A and B are logged in different order depends on whether there's an `await` creating a microtask checkpoint or not. `await? true` won't create a microtask checkpoint, so you'll get the two() behavior, printing B then A. `await? Promise.resolve(true)` will create one, so you'll get the one() behavior, printing A then B. > If `maybeAsync` returns twice non promises results, there is only one > microtask within the async `tasks` function, that would linearly collect all > non promises, so that above example could have 1, 2, max 4 microtasks, > instead of always 4 > . > To explain `await?` in steps: > > * is the awaited value a promise? schedule microtask > * otherwise schedule a single microtask if none has been scheduled already, > or queue this result to the previous scheduled one > > This would grant same linear order and save time. Ah, your earlier posts didn't say that `await? nonPromise` *would* schedule a microtask in some cases! That does change things. Hm, I wonder if this is observably different from the current behavior? ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Conditional await, anyone?
> What's the order of the logs? Exactly the same, as the `await?` is inevitably inside an `async` function which would grant a single microtask instead of N. Example: ```js async function tasks() { await? maybeAsync(); await? maybeAsync(); await? maybeAsync(); } tasks(); ``` If `maybeAsync` returns twice non promises results, there is only one microtask within the async `tasks` function, that would linearly collect all non promises, so that above example could have 1, 2, max 4 microtasks, instead of always 4 . To explain `await?` in steps: * is the awaited value a promise? schedule microtask * otherwise schedule a single microtask if none has been scheduled already, or queue this result to the previous scheduled one This would grant same linear order and save time. However, like others said already in the twitter thread, we all wish `await` was already working like that by default, while it seems to unconditionally create micro tasks even when it's not strictly necessary. On Wed, Oct 9, 2019 at 11:47 PM Tab Atkins Jr. wrote: > On Wed, Oct 9, 2019 at 12:08 AM Andrea Giammarchi > wrote: > > I don't know why this went in a completely unrelated direction so ... > I'll try to explain again what is `await?` about. > > Nah, we got it. Our complaint was still about the semantics. > > > ```js > > const value = await? callback(); > > > > // as sugar for > > let value = callback(); > > if ('then' in value) > > value = await value; > > ``` > > > > The order is guaranteed and linear in every case, so that nothing > actually change logically speaking, and the hint would be about > performance, 'cause engines don't apparently optimize non-promise based > cases. > > Expand that code so there's a caller: > > ```js > function one() { > two(); > console.log("one"); > } > async function two() { > await? maybeAsync(); > console.log("two"); > } > ``` > > What's the order of the logs? > > If maybeAsync() is synchronous, then one() calls two(), two() calls > maybeAsync() which returns immediately, and it continues to log "two" > before ending and returning execution to one(), which then logs "one" > and ends. > > If maybeAsync() returns a promise, then one() calls two(), two calls > maybeAsync() then freezes while it waits for the promise to resolve, > returning execution to one(). Since one() isn't awaiting the promise > returned by two(), it just immediately continues and logs "one" then > ends. At some point later in execution, the maybeAsync() promise > resolves, two() unfreezes, then it logs "two". > > So no, the order is not guaranteed. It's unpredictable and depends on > whether the function being await?'d returns a promise or not. If you > don't know what maybeAsync() returns, you won't be able to predict > your own execution flow, which is dangerous and a very likely source > of bugs. > > ~TJ > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Conditional await, anyone?
On Wed, Oct 9, 2019 at 12:08 AM Andrea Giammarchi wrote: > I don't know why this went in a completely unrelated direction so ... I'll > try to explain again what is `await?` about. Nah, we got it. Our complaint was still about the semantics. > ```js > const value = await? callback(); > > // as sugar for > let value = callback(); > if ('then' in value) > value = await value; > ``` > > The order is guaranteed and linear in every case, so that nothing actually > change logically speaking, and the hint would be about performance, 'cause > engines don't apparently optimize non-promise based cases. Expand that code so there's a caller: ```js function one() { two(); console.log("one"); } async function two() { await? maybeAsync(); console.log("two"); } ``` What's the order of the logs? If maybeAsync() is synchronous, then one() calls two(), two() calls maybeAsync() which returns immediately, and it continues to log "two" before ending and returning execution to one(), which then logs "one" and ends. If maybeAsync() returns a promise, then one() calls two(), two calls maybeAsync() then freezes while it waits for the promise to resolve, returning execution to one(). Since one() isn't awaiting the promise returned by two(), it just immediately continues and logs "one" then ends. At some point later in execution, the maybeAsync() promise resolves, two() unfreezes, then it logs "two". So no, the order is not guaranteed. It's unpredictable and depends on whether the function being await?'d returns a promise or not. If you don't know what maybeAsync() returns, you won't be able to predict your own execution flow, which is dangerous and a very likely source of bugs. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Conditional await, anyone?
Hi! First, experiences of this guy https://medium.com/@bluepnume/intentionally-unleashing-zalgo-with-promises-ab3f63ead2fd seem to refute the problematicity of zalgo. Second, I actually have the use case of this pattern (though actually it's not a use case for a new syntax). In Amber Smalltalk (implementation running of top of JS engine), it would immensely help to be able to have classical "proxied message send" case, where message send is asynchronous in the background. As Amber compiles the code itself, you may say "compile it so you simply await every message send", but then, code that _must be_ synchronous (callbacks used in external JS libs API) will fail. Having two modes is not an option. So I would really use the option to have message sends synchronous by default, but in the case they _can be_ asynchronous, to be able to let them be that way. Actually, the paypal guy mentioned in the first paragraph has similar case (inter-frame RPC). Using his ZalgoPromise I could compile the things like ZalgoPromise.resolve(make the message send).then(function (result) ... Herby On 8. 10. 2019 22:25, Tab Atkins Jr. wrote: I'm not sure I understand the intended use-case here. If the author knows the function they're calling is async, they can use `await` normally. If they know it's not async, they can avoid `await` altogether. If they have no idea whether it's async or not, that means they just don't understand what the function is returning, which sounds like a really bad thing that they should fix? And in that case, as Gus says, `await?`'s semantics would do some confusing things to execution order, making the line after the `await?` either run *before* or *after* the calling code, depending on whether the await'd value was a promise or not. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Conditional await, anyone?
I had a similar example in real-world code, but it was just to merge sync and async into the same code path. I handled it by using generators and basically running them myself: https://github.com/isiahmeadows/mithril-node-render/blob/v2/index.js#L195-L206 In either case, I'm not sure it's worth adding new syntax for it. - Isiah Meadows cont...@isiahmeadows.com www.isiahmeadows.com On Wed, Oct 9, 2019 at 3:08 AM Andrea Giammarchi wrote: > > I don't know why this went in a completely unrelated direction so ... I'll > try to explain again what is `await?` about. > > My first two examples show a relevant performance difference between the > async code and the sync one. > > The async code though, has zero reasons to be async and so much slower. > > ```js > (async () => { > console.time('await'); > const result = await (async () => [await 1, await 2, await 3])(); > console.timeEnd('await'); > return result; > })(); > ``` > > Why would `await 1` ever need to create a micro task, if already executed > into a scheduled one via async? > > Or in general, why any callback that would early return a value that is not a > promise should create a micro task? > > So the proposal was implemented in an attempt to de-sugar `await?` into the > steps proposed by the dev I've interacted with: > > ```js > const value = await? callback(); > > // as sugar for > let value = callback(); > if ('then' in value) > value = await value; > ``` > > The order is guaranteed and linear in every case, so that nothing actually > change logically speaking, and the hint would be about performance, 'cause > engines don't apparently optimize non-promise based cases. > > However, since the initial intent/proposal about performance got translated > into everything else, I've instantly lost interest myself as it's evident an > `await?` would causes more confusion than it solves. > > I am also not answering other points as not relevant for this idea/proposal. > > Thanks regardless for sharing your thoughts, it helped me see it would > confuse developers. > > Best Regards > > > > > > On Tue, Oct 8, 2019 at 11:58 PM Dan Peddle wrote: >> >> Have to agree, mixing sync and async code like this looks like a disaster >> waiting to happen. Knowing which order your code will be executed in might >> seem not so important for controlled environments where micro optimisations >> are attractive, but thinking about trying to track down a bug through this >> would drive me nuts. >> >> Imagine you have a cached value which can be retrieved synchronously - other >> code which runs in order, and perhaps not directly part of this chain, would >> be fine. When it’s not there, zalgo would indeed be released. The solution >> to this is to use promises (as I’m sure you know) so you have a consistent >> way of saying when something is ready... otherwise it’s thenable sniffing >> all the way through the codebase. >> >> Async infers some kind of IO or deferred process, scheduling. If there’s a >> dependency, then we need to express that. That it may be in some cases >> available synchronously seems like something to be extremely wary of. >> >> > On 8. Oct 2019, at 22:25, Tab Atkins Jr. wrote: >> > >> > I'm not sure I understand the intended use-case here. If the author >> > knows the function they're calling is async, they can use `await` >> > normally. If they know it's not async, they can avoid `await` >> > altogether. If they have no idea whether it's async or not, that means >> > they just don't understand what the function is returning, which >> > sounds like a really bad thing that they should fix? And in that >> > case, as Gus says, `await?`'s semantics would do some confusing things >> > to execution order, making the line after the `await?` either run >> > *before* or *after* the calling code, depending on whether the await'd >> > value was a promise or not. >> > >> > ~TJ >> > ___ >> > es-discuss mailing list >> > es-discuss@mozilla.org >> > https://mail.mozilla.org/listinfo/es-discuss > > ___ > es-discuss mailing list > es-discuss@mozilla.org > https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Conditional await, anyone?
I don't know why this went in a completely unrelated direction so ... I'll try to explain again what is `await?` about. My first two examples show a relevant performance difference between the async code and the sync one. The async code though, has zero reasons to be async and so much slower. ```js (async () => { console.time('await'); const result = await (async () => [await 1, await 2, await 3])(); console.timeEnd('await'); return result; })(); ``` Why would `await 1` ever need to create a micro task, if already executed into a scheduled one via async? Or in general, why any callback that would early return a value that is not a promise should create a micro task? So the proposal was implemented in an attempt to de-sugar `await?` into the steps proposed by the dev I've interacted with: ```js const value = await? callback(); // as sugar for let value = callback(); if ('then' in value) value = await value; ``` The order is guaranteed and linear in every case, so that nothing actually change logically speaking, and the hint would be about performance, 'cause engines don't apparently optimize non-promise based cases. However, since the initial intent/proposal about performance got translated into everything else, I've instantly lost interest myself as it's evident an `await?` would causes more confusion than it solves. I am also not answering other points as not relevant for this idea/proposal. Thanks regardless for sharing your thoughts, it helped me see it would confuse developers. Best Regards On Tue, Oct 8, 2019 at 11:58 PM Dan Peddle wrote: > Have to agree, mixing sync and async code like this looks like a disaster > waiting to happen. Knowing which order your code will be executed in might > seem not so important for controlled environments where micro optimisations > are attractive, but thinking about trying to track down a bug through this > would drive me nuts. > > Imagine you have a cached value which can be retrieved synchronously - > other code which runs in order, and perhaps not directly part of this > chain, would be fine. When it’s not there, zalgo would indeed be released. > The solution to this is to use promises (as I’m sure you know) so you have > a consistent way of saying when something is ready... otherwise it’s > thenable sniffing all the way through the codebase. > > Async infers some kind of IO or deferred process, scheduling. If there’s a > dependency, then we need to express that. That it may be in some cases > available synchronously seems like something to be extremely wary of. > > > On 8. Oct 2019, at 22:25, Tab Atkins Jr. wrote: > > > > I'm not sure I understand the intended use-case here. If the author > > knows the function they're calling is async, they can use `await` > > normally. If they know it's not async, they can avoid `await` > > altogether. If they have no idea whether it's async or not, that means > > they just don't understand what the function is returning, which > > sounds like a really bad thing that they should fix? And in that > > case, as Gus says, `await?`'s semantics would do some confusing things > > to execution order, making the line after the `await?` either run > > *before* or *after* the calling code, depending on whether the await'd > > value was a promise or not. > > > > ~TJ > > ___ > > es-discuss mailing list > > es-discuss@mozilla.org > > https://mail.mozilla.org/listinfo/es-discuss > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Conditional await, anyone?
Have to agree, mixing sync and async code like this looks like a disaster waiting to happen. Knowing which order your code will be executed in might seem not so important for controlled environments where micro optimisations are attractive, but thinking about trying to track down a bug through this would drive me nuts. Imagine you have a cached value which can be retrieved synchronously - other code which runs in order, and perhaps not directly part of this chain, would be fine. When it’s not there, zalgo would indeed be released. The solution to this is to use promises (as I’m sure you know) so you have a consistent way of saying when something is ready... otherwise it’s thenable sniffing all the way through the codebase. Async infers some kind of IO or deferred process, scheduling. If there’s a dependency, then we need to express that. That it may be in some cases available synchronously seems like something to be extremely wary of. > On 8. Oct 2019, at 22:25, Tab Atkins Jr. wrote: > > I'm not sure I understand the intended use-case here. If the author > knows the function they're calling is async, they can use `await` > normally. If they know it's not async, they can avoid `await` > altogether. If they have no idea whether it's async or not, that means > they just don't understand what the function is returning, which > sounds like a really bad thing that they should fix? And in that > case, as Gus says, `await?`'s semantics would do some confusing things > to execution order, making the line after the `await?` either run > *before* or *after* the calling code, depending on whether the await'd > value was a promise or not. > > ~TJ > ___ > es-discuss mailing list > es-discuss@mozilla.org > https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Conditional await, anyone?
I'm not sure I understand the intended use-case here. If the author knows the function they're calling is async, they can use `await` normally. If they know it's not async, they can avoid `await` altogether. If they have no idea whether it's async or not, that means they just don't understand what the function is returning, which sounds like a really bad thing that they should fix? And in that case, as Gus says, `await?`'s semantics would do some confusing things to execution order, making the line after the `await?` either run *before* or *after* the calling code, depending on whether the await'd value was a promise or not. ~TJ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Conditional await, anyone?
> Sure thing, engines might infer returned values in some hot code and skip the microtask dance once it's sure some callback might return values that are not promises, but what if developers could give hints about this possibility? Engines can't do this, because it would change the observable order of running code. > so ... how about accepting a question mark after `await` so that it's clear the developer knows the function might return non Promise based results, hence the code could be faster? This is actually considered an antipattern (google "zalgo javascript"), and promises try to stop that from happening. On Tue, Oct 8, 2019 at 10:34 AM Andrea Giammarchi < andrea.giammar...@gmail.com> wrote: > When developers use `async` functions, they'll likely also await > unconditionally any callback, even if such callback could return a non > promise value, for whatever reason. > > Engines are great at optimizing stuff, but as of today, if we measure the > performance difference between this code: > > ```js > (async () => { > console.time('await'); > const result = await (async () => [await 1, await 2, await 3])(); > console.timeEnd('await'); > return result; > })(); > ``` > > and the following one: > > ```js > (/* sync */ () => { > console.time('sync'); > const result = (() => [1, 2, 3])(); > console.timeEnd('sync'); > return result; > })(); > ``` > > we'll notice the latter is about 10 to 100 times faster than the > asynchronous one. > > Sure thing, engines might infer returned values in some hot code and skip > the microtask dance once it's sure some callback might return values that > are not promises, but what if developers could give hints about this > possibility? > > In a twitter exchange, one dev mentioned the following: > > > I often write: > > let value = mightBePromise() > > if (value && value.then) > > value = await value > > in hot code in my async functions, for precisely this reason (I too have > measured the large perf difference). > > so ... how about accepting a question mark after `await` so that it's > clear the developer knows the function might return non Promise based > results, hence the code could be faster? > > ```js > const value = await? callback(); > ``` > > Above code is basically what's this thread/proposal about, adding a > conditional `await` syntax, so that if the returned value is not thenable, > there's no reason to waste a microtask on it. > > What are your thoughts? > > > ___ > es-discuss mailing list > es-discuss@mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss