Looking at my project (in which asynchronous calls are entirely donevia generators), I can't see how async/await would simplify code forend-users like me (application programmers).
End users write the spawn()/Q.async()/co() wrapperat most onesingle time in an application:
To use the example atstrawman:async_functions
asyncfunctionchainAnimationsAsync(elem, animations){ CODE; }is just
functionchainAnimationsAsync*(elem, animations){ same CODE; }when flow control is done by a framework or at the entry point to yourapplication. spawn() isn't needed.
I can't see how this will reduce application's code even a little. Somy question is, is async/await needed?
One more question
yield is practically very difficult to use in a project because youdon't get proper stack traces (at least with the current flow controllibraries). You'd only see the last call which threw the error, andthen functions from the flow control library immediately below that. Isuppose the generators leading up to the erring generator are allsuspended and wouldn't be on the stack frame chain.
yield* generator delegation solves this problem, you get real stacktraces. I was able to get full stack traces simply by replacing allyield X() with yield* X()
example code as in:chopachom/syncio
So if there are valid use-cases for adding async/await to JS,shouldn't it be based on how yield* works rather than yield?
Looking at my project (in which asynchronous calls are entirely donevia generators), I can't see how async/await would simplify code forend-users like me (application programmers).End users write the spawn()/Q.async()/co() wrapper *at most* onesingle time in an application:1. When using a framework like say koajs, you don't have to write it even once.2. While not using a framework, you'd have to use the wrapper onesingle time in say, the main.js file.To use the example athttp://wiki.ecmascript.org/doku.php?id=strawman:async_functionsasync function chainAnimationsAsync(elem, animations) { CODE; }is justfunction chainAnimationsAsync*(elem, animations) { same CODE; }when flow control is done by a framework or at the entry point to yourapplication. spawn() isn't needed.I can't see how this will reduce application's code even a little. Somy question is, is async/await needed?One more question--------------------------1. yield is practically very difficult to use in a project because youdon't get proper stack traces (at least with the current flow controllibraries). You'd only see the last call which threw the error, andthen functions from the flow control library immediately below that. Isuppose the generators leading up to the erring generator are allsuspended and wouldn't be on the stack frame chain.2. yield* generator delegation solves this problem, you get real stacktraces. I was able to get full stack traces simply by replacing allyield X() with yield* X()example code as in: https://github.com/chopachom/syncioSo if there are valid use-cases for adding async/await to JS,shouldn't it be based on how yield* works rather than yield?-- JesThe Fora Project is coming...https://github.com/jeswin/foraYes, async/await solves one problem in particular that generators alonecannot—the ability toawait some asynchronous value from within a truegenerator (one yielding actual values, not a coroutine yielding promises orthunks into a trampoline). These coro trampolines are a clever hack, butthe async/await syntax allows these two completely distinct languagefeatures to become truly orthogonal. This is important in its own right,but also has immediately utility (e.g. more elegant lazy streams usingfor-of iteration).
Yes, async/await solves one problem in particular that generators alonecannot—the ability to `await` some asynchronous value from within a truegenerator (one yielding actual values, not a coroutine yielding promises orthunks into a trampoline). These coro trampolines are a clever hack, butthe async/await syntax allows these two completely distinct languagefeatures to become truly orthogonal. This is important in its own right,but also has immediately utility (e.g. more elegant lazy streams usingfor-of iteration).On Thu, Sep 11, 2014 at 5:45 AM, Jeswin Kumar <jeswinpk at agilehead.com>wrote:> Looking at my project (in which asynchronous calls are entirely done> via generators), I can't see how async/await would simplify code for> end-users like me (application programmers).>> End users write the spawn()/Q.async()/co() wrapper *at most* one> single time in an application:> 1. When using a framework like say koajs, you don't have to write it even> once.> 2. While not using a framework, you'd have to use the wrapper one> single time in say, the main.js file.>> To use the example at> http://wiki.ecmascript.org/doku.php?id=strawman:async_functions>> async function chainAnimationsAsync(elem, animations) { CODE; }> is just> function chainAnimationsAsync*(elem, animations) { same CODE; }> when flow control is done by a framework or at the entry point to your> application. spawn() isn't needed.>> I can't see how this will reduce application's code even a little. So> my question is, is async/await needed?>>> One more question> --------------------------> 1. yield is practically very difficult to use in a project because you> don't get proper stack traces (at least with the current flow control> libraries). You'd only see the last call which threw the error, and> then functions from the flow control library immediately below that. I> suppose the generators leading up to the erring generator are all> suspended and wouldn't be on the stack frame chain.>> 2. yield* generator delegation solves this problem, you get real stack> traces. I was able to get full stack traces simply by replacing all> yield X() with yield* X()>> example code as in: https://github.com/chopachom/syncio>> So if there are valid use-cases for adding async/await to JS,> shouldn't it be based on how yield* works rather than yield?>> -- Jes>> The Fora Project is coming...> https://github.com/jeswin/fora> _______________________________________________> es-discuss mailing list> es-discuss at mozilla.org> https://mail.mozilla.org/listinfo/es-discuss>-------------- next part --------------An HTML attachment was scrubbed...URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140911/a938d6b9/attachment.html>There are several problems solved by async/await instead of twisting generators:
What if you wanted to use generators for lazy sequences (iterables), instead of asynchronicity? If your framework assumes all generators are for async, you lose the original use case of generators.
Say what you mean.function* andyield mean something very different fromasync function andawait, similar to howSubclass.prototype = Object.create(Superclass.prototype); Subclass.prototype.constructor = Subclass is different fromclass Subclass extends Superclass.
Operator precedence. You can doawait a + await b to mean(await a) + (await b), butyield a + yield b meansyield (a + (yield b)).
Ability to produce promise-returning functions without buying into a specific framework that interprets generators in a certain way. E.g., you could useasync function f() { return 5; } to return a promise for 5, which people can consume withf().then(v => ...). If you try to dofunction* f() { return 5; } you will get an iterable, which is not understood to be asynchronous. (Hopefully my use ofreturn 5 for brevity instead of more complex code does not confuse this point for you.)
As for stack traces, long stack trace support is a debugging feature, and the fact thatyield* gets them right now doesn't mean thatawait won't get them in the future.
There are several problems solved by async/await instead of twisting generators:1. What if you wanted to use generators for lazy sequences (iterables), instead of asynchronicity? If your framework assumes all generators are for async, you lose the original use case of generators.2. Say what you mean. `function*` and `yield` mean something very different from `async function` and `await`, similar to how `Subclass.prototype = Object.create(Superclass.prototype); Subclass.prototype.constructor = Subclass` is different from `class Subclass extends Superclass`.3. Operator precedence. You can do `await a + await b` to mean `(await a) + (await b)`, but `yield a + yield b` means `yield (a + (yield b))`.4. Ability to produce promise-returning functions without buying into a specific framework that interprets generators in a certain way. E.g., you could use `async function f() { return 5; }` to return a promise for 5, which people can consume with `f().then(v => ...)`. If you try to do `function* f() { return 5; }` you will get an iterable, which is not understood to be asynchronous. (Hopefully my use of `return 5` for brevity instead of more complex code does not confuse this point for you.)As for stack traces, long stack trace support is a debugging feature, and the fact that `yield*` gets them right now doesn't mean that `await` won't get them in the future.-----Original Message-----From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Jeswin KumarSent: Thursday, September 11, 2014 11:46To: es-discuss at mozilla.orgSubject: Does async/await solve a real problem?Looking at my project (in which asynchronous calls are entirely done via generators), I can't see how async/await would simplify code for end-users like me (application programmers).End users write the spawn()/Q.async()/co() wrapper *at most* one single time in an application:1. When using a framework like say koajs, you don't have to write it even once.2. While not using a framework, you'd have to use the wrapper one single time in say, the main.js file.To use the example athttp://wiki.ecmascript.org/doku.php?id=strawman:async_functionsasync function chainAnimationsAsync(elem, animations) { CODE; } is just function chainAnimationsAsync*(elem, animations) { same CODE; } when flow control is done by a framework or at the entry point to your application. spawn() isn't needed.I can't see how this will reduce application's code even a little. So my question is, is async/await needed?One more question--------------------------1. yield is practically very difficult to use in a project because you don't get proper stack traces (at least with the current flow control libraries). You'd only see the last call which threw the error, and then functions from the flow control library immediately below that. I suppose the generators leading up to the erring generator are all suspended and wouldn't be on the stack frame chain.2. yield* generator delegation solves this problem, you get real stack traces. I was able to get full stack traces simply by replacing all yield X() with yield* X()example code as in: https://github.com/chopachom/syncioSo if there are valid use-cases for adding async/await to JS, shouldn't it be based on how yield* works rather than yield?-- JesThe Fora Project is coming...https://github.com/jeswin/fora_______________________________________________es-discuss mailing listes-discuss at mozilla.orghttps://mail.mozilla.org/listinfo/es-discussAlso, seelukehoban/ecmascript-asyncawait#14 forprevious discussion.
Also, see https://github.com/lukehoban/ecmascript-asyncawait/issues/14 forprevious discussion.On Thu, Sep 11, 2014 at 8:42 AM, Domenic Denicola <domenic at domenicdenicola.com> wrote:> There are several problems solved by async/await instead of twisting> generators:>> 1. What if you wanted to use generators for lazy sequences (iterables),> instead of asynchronicity? If your framework assumes all generators are for> async, you lose the original use case of generators.>> 2. Say what you mean. `function*` and `yield` mean something very> different from `async function` and `await`, similar to how> `Subclass.prototype = Object.create(Superclass.prototype);> Subclass.prototype.constructor = Subclass` is different from `class> Subclass extends Superclass`.>> 3. Operator precedence. You can do `await a + await b` to mean `(await a)> + (await b)`, but `yield a + yield b` means `yield (a + (yield b))`.>> 4. Ability to produce promise-returning functions without buying into a> specific framework that interprets generators in a certain way. E.g., you> could use `async function f() { return 5; }` to return a promise for 5,> which people can consume with `f().then(v => ...)`. If you try to do> `function* f() { return 5; }` you will get an iterable, which is not> understood to be asynchronous. (Hopefully my use of `return 5` for brevity> instead of more complex code does not confuse this point for you.)>> As for stack traces, long stack trace support is a debugging feature, and> the fact that `yield*` gets them right now doesn't mean that `await` won't> get them in the future.>> -----Original Message-----> From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of> Jeswin Kumar> Sent: Thursday, September 11, 2014 11:46> To: es-discuss at mozilla.org> Subject: Does async/await solve a real problem?>> Looking at my project (in which asynchronous calls are entirely done via> generators), I can't see how async/await would simplify code for end-users> like me (application programmers).>> End users write the spawn()/Q.async()/co() wrapper *at most* one single> time in an application:> 1. When using a framework like say koajs, you don't have to write it even> once.> 2. While not using a framework, you'd have to use the wrapper one single> time in say, the main.js file.>> To use the example at> http://wiki.ecmascript.org/doku.php?id=strawman:async_functions>> async function chainAnimationsAsync(elem, animations) { CODE; } is just> function chainAnimationsAsync*(elem, animations) { same CODE; } when flow> control is done by a framework or at the entry point to your application.> spawn() isn't needed.>> I can't see how this will reduce application's code even a little. So my> question is, is async/await needed?>>> One more question> --------------------------> 1. yield is practically very difficult to use in a project because you> don't get proper stack traces (at least with the current flow control> libraries). You'd only see the last call which threw the error, and then> functions from the flow control library immediately below that. I suppose> the generators leading up to the erring generator are all suspended and> wouldn't be on the stack frame chain.>> 2. yield* generator delegation solves this problem, you get real stack> traces. I was able to get full stack traces simply by replacing all yield> X() with yield* X()>> example code as in: https://github.com/chopachom/syncio>> So if there are valid use-cases for adding async/await to JS, shouldn't it> be based on how yield* works rather than yield?>> -- Jes>> The Fora Project is coming...> https://github.com/jeswin/fora> _______________________________________________> es-discuss mailing list> es-discuss at mozilla.org> https://mail.mozilla.org/listinfo/es-discuss> _______________________________________________> es-discuss mailing list> es-discuss at mozilla.org> https://mail.mozilla.org/listinfo/es-discuss>-------------- next part --------------An HTML attachment was scrubbed...URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140911/60965058/attachment.html>await has also another problem in that if somewhere, deep down the callstack, something is intending to do async, then up the entire call stackeverywhere you've got to insert await. It's a bit of a headache for codemaintenance (hello bicycle repair man jam session), and it's also fairlyunfriendly for library authors.
There is a solution to that problem, which is not using generators if you'dwant co-routines. If you want co-routine like behavior, please implementco-routines (and you can prop whatever scheduling/managing on top of that).
An argument has been made in earlier discussions on that topic, that JS VMscan't deal with co-routines (garbage collection, DOM, whatever). Butsurely, if the VM can support generators/continuations, it could supportfull co-routines.
I'd recommend python's greenlet as an outstanding implementation of aco-routine interface that includes basically everything one could wish fromit. I'd not consider python 3's "asyncio" a proper co-routineimplementation (i.e. it's the same generator/await hack as is beingdiscussed here).
await has also another problem in that if somewhere, deep down the callstack, something is intending to do async, then up the entire call stackeverywhere you've got to insert await. It's a bit of a headache for codemaintenance (hello bicycle repair man jam session), and it's also fairlyunfriendly for library authors.There is a solution to that problem, which is not using generators if you'dwant co-routines. If you want co-routine like behavior, please implementco-routines (and you can prop whatever scheduling/managing on top of that).An argument has been made in earlier discussions on that topic, that JS VMscan't deal with co-routines (garbage collection, DOM, whatever). Butsurely, if the VM can support generators/continuations, it could supportfull co-routines.I'd recommend python's greenlet as an outstanding implementation of aco-routine interface that includes basically everything one could wish fromit. I'd not consider python 3's "asyncio" a proper co-routineimplementation (i.e. it's the same generator/await hack as is beingdiscussed here).On Thu, Sep 11, 2014 at 3:05 PM, Kevin Smith <zenparsing at gmail.com> wrote:> Also, see https://github.com/lukehoban/ecmascript-asyncawait/issues/14> for previous discussion.>> On Thu, Sep 11, 2014 at 8:42 AM, Domenic Denicola <> domenic at domenicdenicola.com> wrote:>>> There are several problems solved by async/await instead of twisting>> generators:>>>> 1. What if you wanted to use generators for lazy sequences (iterables),>> instead of asynchronicity? If your framework assumes all generators are for>> async, you lose the original use case of generators.>>>> 2. Say what you mean. `function*` and `yield` mean something very>> different from `async function` and `await`, similar to how>> `Subclass.prototype = Object.create(Superclass.prototype);>> Subclass.prototype.constructor = Subclass` is different from `class>> Subclass extends Superclass`.>>>> 3. Operator precedence. You can do `await a + await b` to mean `(await a)>> + (await b)`, but `yield a + yield b` means `yield (a + (yield b))`.>>>> 4. Ability to produce promise-returning functions without buying into a>> specific framework that interprets generators in a certain way. E.g., you>> could use `async function f() { return 5; }` to return a promise for 5,>> which people can consume with `f().then(v => ...)`. If you try to do>> `function* f() { return 5; }` you will get an iterable, which is not>> understood to be asynchronous. (Hopefully my use of `return 5` for brevity>> instead of more complex code does not confuse this point for you.)>>>> As for stack traces, long stack trace support is a debugging feature, and>> the fact that `yield*` gets them right now doesn't mean that `await` won't>> get them in the future.>>>> -----Original Message----->> From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of>> Jeswin Kumar>> Sent: Thursday, September 11, 2014 11:46>> To: es-discuss at mozilla.org>> Subject: Does async/await solve a real problem?>>>> Looking at my project (in which asynchronous calls are entirely done via>> generators), I can't see how async/await would simplify code for end-users>> like me (application programmers).>>>> End users write the spawn()/Q.async()/co() wrapper *at most* one single>> time in an application:>> 1. When using a framework like say koajs, you don't have to write it even>> once.>> 2. While not using a framework, you'd have to use the wrapper one single>> time in say, the main.js file.>>>> To use the example at>> http://wiki.ecmascript.org/doku.php?id=strawman:async_functions>>>> async function chainAnimationsAsync(elem, animations) { CODE; } is just>> function chainAnimationsAsync*(elem, animations) { same CODE; } when flow>> control is done by a framework or at the entry point to your application.>> spawn() isn't needed.>>>> I can't see how this will reduce application's code even a little. So my>> question is, is async/await needed?>>>>>> One more question>> -------------------------->> 1. yield is practically very difficult to use in a project because you>> don't get proper stack traces (at least with the current flow control>> libraries). You'd only see the last call which threw the error, and then>> functions from the flow control library immediately below that. I suppose>> the generators leading up to the erring generator are all suspended and>> wouldn't be on the stack frame chain.>>>> 2. yield* generator delegation solves this problem, you get real stack>> traces. I was able to get full stack traces simply by replacing all yield>> X() with yield* X()>>>> example code as in: https://github.com/chopachom/syncio>>>> So if there are valid use-cases for adding async/await to JS, shouldn't>> it be based on how yield* works rather than yield?>>>> -- Jes>>>> The Fora Project is coming...>> https://github.com/jeswin/fora>> _______________________________________________>> es-discuss mailing list>> es-discuss at mozilla.org>> https://mail.mozilla.org/listinfo/es-discuss>> _______________________________________________>> es-discuss mailing list>> es-discuss at mozilla.org>> https://mail.mozilla.org/listinfo/es-discuss>>>>> _______________________________________________> es-discuss mailing list> es-discuss at mozilla.org> https://mail.mozilla.org/listinfo/es-discuss>>-------------- next part --------------An HTML attachment was scrubbed...URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140911/da83394f/attachment.html>VM issues are not the argument against coroutines or deep generators. Theissue is that unpredictable interleaving makes reasoning about invariantsmuch too difficult. Without these, we have an important guarantee: When fsynchronously calls g, the only side effects that might have occurred bythe time g returns to f are those g might have caused. Thus, these are theonly side effect possibilities that f must worry about.
See section 18.3 ofwww.erights.org/talks/thesis/markm-thesis.pdf and replace postfix diagonal uparrow with prefix "await". In ES7 the example would be
async function foo() { return bar(await getint(), y()); }... await foo() ...The net effect is like co-routines, except that the placement of "async"and "await" -- like the diagonal uparrow in the text -- marks the placeswhere interleaving might occur. This is as close to coroutine support as weshould ever come.
On Thu, Sep 11, 2014 at 6:20 AM, Florian Bösch <pyalot at gmail.com> wrote:> await has also another problem in that if somewhere, deep down the call> stack, something is intending to do async, then up the entire call stack> everywhere you've got to insert await. It's a bit of a headache for code> maintenance (hello bicycle repair man jam session), and it's also fairly> unfriendly for library authors.>> There is a solution to that problem, which is not using generators if> you'd want co-routines. If you want co-routine like behavior, please> implement co-routines (and you can prop whatever scheduling/managing on top> of that).>> An argument has been made in earlier discussions on that topic, that JS> VMs can't deal with co-routines (garbage collection, DOM, whatever). But> surely, if the VM can support generators/continuations, it could support> full co-routines.>VM issues are not the argument against coroutines or deep generators. Theissue is that unpredictable interleaving makes reasoning about invariantsmuch too difficult. Without these, we have an important guarantee: When fsynchronously calls g, the only side effects that might have occurred bythe time g returns to f are those g might have caused. Thus, these are theonly side effect possibilities that f must worry about.See section 18.3 of <http://www.erights.org/talks/thesis/markm-thesis.pdf>and replace postfix diagonal uparrow with prefix "await". In ES7 theexample would be async function foo() { return bar(await getint(), y()); } ... await foo() ...The net effect is like co-routines, except that the placement of "async"and "await" -- like the diagonal uparrow in the text -- marks the placeswhere interleaving might occur. This is as close to coroutine support as weshould ever come.> I'd recommend python's greenlet as an outstanding implementation of a> co-routine interface that includes basically everything one could wish from> it. I'd not consider python 3's "asyncio" a proper co-routine> implementation (i.e. it's the same generator/await hack as is being> discussed here).>> On Thu, Sep 11, 2014 at 3:05 PM, Kevin Smith <zenparsing at gmail.com> wrote:>>> Also, see https://github.com/lukehoban/ecmascript-asyncawait/issues/14>> for previous discussion.>>>> On Thu, Sep 11, 2014 at 8:42 AM, Domenic Denicola <>> domenic at domenicdenicola.com> wrote:>>>>> There are several problems solved by async/await instead of twisting>>> generators:>>>>>> 1. What if you wanted to use generators for lazy sequences (iterables),>>> instead of asynchronicity? If your framework assumes all generators are for>>> async, you lose the original use case of generators.>>>>>> 2. Say what you mean. `function*` and `yield` mean something very>>> different from `async function` and `await`, similar to how>>> `Subclass.prototype = Object.create(Superclass.prototype);>>> Subclass.prototype.constructor = Subclass` is different from `class>>> Subclass extends Superclass`.>>>>>> 3. Operator precedence. You can do `await a + await b` to mean `(await>>> a) + (await b)`, but `yield a + yield b` means `yield (a + (yield b))`.>>>>>> 4. Ability to produce promise-returning functions without buying into a>>> specific framework that interprets generators in a certain way. E.g., you>>> could use `async function f() { return 5; }` to return a promise for 5,>>> which people can consume with `f().then(v => ...)`. If you try to do>>> `function* f() { return 5; }` you will get an iterable, which is not>>> understood to be asynchronous. (Hopefully my use of `return 5` for brevity>>> instead of more complex code does not confuse this point for you.)>>>>>> As for stack traces, long stack trace support is a debugging feature,>>> and the fact that `yield*` gets them right now doesn't mean that `await`>>> won't get them in the future.>>>>>> -----Original Message----->>> From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of>>> Jeswin Kumar>>> Sent: Thursday, September 11, 2014 11:46>>> To: es-discuss at mozilla.org>>> Subject: Does async/await solve a real problem?>>>>>> Looking at my project (in which asynchronous calls are entirely done via>>> generators), I can't see how async/await would simplify code for end-users>>> like me (application programmers).>>>>>> End users write the spawn()/Q.async()/co() wrapper *at most* one single>>> time in an application:>>> 1. When using a framework like say koajs, you don't have to write it>>> even once.>>> 2. While not using a framework, you'd have to use the wrapper one single>>> time in say, the main.js file.>>>>>> To use the example at>>> http://wiki.ecmascript.org/doku.php?id=strawman:async_functions>>>>>> async function chainAnimationsAsync(elem, animations) { CODE; } is just>>> function chainAnimationsAsync*(elem, animations) { same CODE; } when flow>>> control is done by a framework or at the entry point to your application.>>> spawn() isn't needed.>>>>>> I can't see how this will reduce application's code even a little. So my>>> question is, is async/await needed?>>>>>>>>> One more question>>> -------------------------->>> 1. yield is practically very difficult to use in a project because you>>> don't get proper stack traces (at least with the current flow control>>> libraries). You'd only see the last call which threw the error, and then>>> functions from the flow control library immediately below that. I suppose>>> the generators leading up to the erring generator are all suspended and>>> wouldn't be on the stack frame chain.>>>>>> 2. yield* generator delegation solves this problem, you get real stack>>> traces. I was able to get full stack traces simply by replacing all yield>>> X() with yield* X()>>>>>> example code as in: https://github.com/chopachom/syncio>>>>>> So if there are valid use-cases for adding async/await to JS, shouldn't>>> it be based on how yield* works rather than yield?>>>>>> -- Jes>>>>>> The Fora Project is coming...>>> https://github.com/jeswin/fora>>> _______________________________________________>>> es-discuss mailing list>>> es-discuss at mozilla.org>>> https://mail.mozilla.org/listinfo/es-discuss>>> _______________________________________________>>> es-discuss mailing list>>> es-discuss at mozilla.org>>> https://mail.mozilla.org/listinfo/es-discuss>>>>>>>>> _______________________________________________>> es-discuss mailing list>> es-discuss at mozilla.org>> https://mail.mozilla.org/listinfo/es-discuss>>>>>> _______________________________________________> es-discuss mailing list> es-discuss at mozilla.org> https://mail.mozilla.org/listinfo/es-discuss>>-- Cheers, --MarkM-------------- next part --------------An HTML attachment was scrubbed...URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140911/39d8470c/attachment-0001.html>A -> B -> C -> D -> E changes to
A -> B -> C -> D -> async E and causes
A await -> B await -> C await -> D await -> async E
And of course if A, B, C or D is used anywhere else it percolates troughthe entire call graph.
Trying to protect people from interlaved code execution effects is noble.But doing so by introducing a rote thing to type everytime you change thecode somewhere underneath is wrong. It's wrong because it breaks logicisolation, it becomes impossible to change part of the library/utiity codewithout this change affecting all code that uses it. This guarantees thatit doesn't happen in practice, because it's too painful to do. It requiresthe code, that uses other code, to know about the internal behavior of thatcode.
If say, I'd propose a semantic that required you to write "foo" in thecode, but just for those pieces of code that contain mentions of "bar", orthat reference code that contains "bar" to the Nth degree, you'd accuse meof trying to introduce a purposefully unusable feature. How is await/asyncnot an unusable feature?
A -> B -> C -> D -> E changes toA -> B -> C -> D -> async E and causesA await -> B await -> C await -> D await -> async EAnd of course if A, B, C or D is used anywhere else it percolates troughthe entire call graph.Trying to protect people from interlaved code execution effects is noble.But doing so by introducing a rote thing to type everytime you change thecode somewhere underneath is wrong. It's wrong because it breaks logicisolation, it becomes impossible to change part of the library/utiity codewithout this change affecting all code that uses it. This guarantees thatit doesn't happen in practice, because it's too painful to do. It requiresthe code, that uses other code, to know about the internal behavior of thatcode.If say, I'd propose a semantic that required you to write "foo" in thecode, but just for those pieces of code that contain mentions of "bar", orthat reference code that contains "bar" to the Nth degree, you'd accuse meof trying to introduce a purposefully unusable feature. How is await/asyncnot an unusable feature?On Thu, Sep 11, 2014 at 4:00 PM, Mark S. Miller <erights at google.com> wrote:> On Thu, Sep 11, 2014 at 6:20 AM, Florian Bösch <pyalot at gmail.com> wrote:>>> await has also another problem in that if somewhere, deep down the call>> stack, something is intending to do async, then up the entire call stack>> everywhere you've got to insert await. It's a bit of a headache for code>> maintenance (hello bicycle repair man jam session), and it's also fairly>> unfriendly for library authors.>>>> There is a solution to that problem, which is not using generators if>> you'd want co-routines. If you want co-routine like behavior, please>> implement co-routines (and you can prop whatever scheduling/managing on top>> of that).>>>> An argument has been made in earlier discussions on that topic, that JS>> VMs can't deal with co-routines (garbage collection, DOM, whatever). But>> surely, if the VM can support generators/continuations, it could support>> full co-routines.>>>> VM issues are not the argument against coroutines or deep generators. The> issue is that unpredictable interleaving makes reasoning about invariants> much too difficult. Without these, we have an important guarantee: When f> synchronously calls g, the only side effects that might have occurred by> the time g returns to f are those g might have caused. Thus, these are the> only side effect possibilities that f must worry about.>> See section 18.3 of <http://www.erights.org/talks/thesis/markm-thesis.pdf>> and replace postfix diagonal uparrow with prefix "await". In ES7 the> example would be>> async function foo() { return bar(await getint(), y()); }> ... await foo() ...>> The net effect is like co-routines, except that the placement of "async"> and "await" -- like the diagonal uparrow in the text -- marks the places> where interleaving might occur. This is as close to coroutine support as we> should ever come.>>>>> I'd recommend python's greenlet as an outstanding implementation of a>> co-routine interface that includes basically everything one could wish from>> it. I'd not consider python 3's "asyncio" a proper co-routine>> implementation (i.e. it's the same generator/await hack as is being>> discussed here).>>>> On Thu, Sep 11, 2014 at 3:05 PM, Kevin Smith <zenparsing at gmail.com>>> wrote:>>>>> Also, see https://github.com/lukehoban/ecmascript-asyncawait/issues/14>>> for previous discussion.>>>>>> On Thu, Sep 11, 2014 at 8:42 AM, Domenic Denicola <>>> domenic at domenicdenicola.com> wrote:>>>>>>> There are several problems solved by async/await instead of twisting>>>> generators:>>>>>>>> 1. What if you wanted to use generators for lazy sequences (iterables),>>>> instead of asynchronicity? If your framework assumes all generators are for>>>> async, you lose the original use case of generators.>>>>>>>> 2. Say what you mean. `function*` and `yield` mean something very>>>> different from `async function` and `await`, similar to how>>>> `Subclass.prototype = Object.create(Superclass.prototype);>>>> Subclass.prototype.constructor = Subclass` is different from `class>>>> Subclass extends Superclass`.>>>>>>>> 3. Operator precedence. You can do `await a + await b` to mean `(await>>>> a) + (await b)`, but `yield a + yield b` means `yield (a + (yield b))`.>>>>>>>> 4. Ability to produce promise-returning functions without buying into a>>>> specific framework that interprets generators in a certain way. E.g., you>>>> could use `async function f() { return 5; }` to return a promise for 5,>>>> which people can consume with `f().then(v => ...)`. If you try to do>>>> `function* f() { return 5; }` you will get an iterable, which is not>>>> understood to be asynchronous. (Hopefully my use of `return 5` for brevity>>>> instead of more complex code does not confuse this point for you.)>>>>>>>> As for stack traces, long stack trace support is a debugging feature,>>>> and the fact that `yield*` gets them right now doesn't mean that `await`>>>> won't get them in the future.>>>>>>>> -----Original Message----->>>> From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of>>>> Jeswin Kumar>>>> Sent: Thursday, September 11, 2014 11:46>>>> To: es-discuss at mozilla.org>>>> Subject: Does async/await solve a real problem?>>>>>>>> Looking at my project (in which asynchronous calls are entirely done>>>> via generators), I can't see how async/await would simplify code for>>>> end-users like me (application programmers).>>>>>>>> End users write the spawn()/Q.async()/co() wrapper *at most* one single>>>> time in an application:>>>> 1. When using a framework like say koajs, you don't have to write it>>>> even once.>>>> 2. While not using a framework, you'd have to use the wrapper one>>>> single time in say, the main.js file.>>>>>>>> To use the example at>>>> http://wiki.ecmascript.org/doku.php?id=strawman:async_functions>>>>>>>> async function chainAnimationsAsync(elem, animations) { CODE; } is just>>>> function chainAnimationsAsync*(elem, animations) { same CODE; } when flow>>>> control is done by a framework or at the entry point to your application.>>>> spawn() isn't needed.>>>>>>>> I can't see how this will reduce application's code even a little. So>>>> my question is, is async/await needed?>>>>>>>>>>>> One more question>>>> -------------------------->>>> 1. yield is practically very difficult to use in a project because you>>>> don't get proper stack traces (at least with the current flow control>>>> libraries). You'd only see the last call which threw the error, and then>>>> functions from the flow control library immediately below that. I suppose>>>> the generators leading up to the erring generator are all suspended and>>>> wouldn't be on the stack frame chain.>>>>>>>> 2. yield* generator delegation solves this problem, you get real stack>>>> traces. I was able to get full stack traces simply by replacing all yield>>>> X() with yield* X()>>>>>>>> example code as in: https://github.com/chopachom/syncio>>>>>>>> So if there are valid use-cases for adding async/await to JS, shouldn't>>>> it be based on how yield* works rather than yield?>>>>>>>> -- Jes>>>>>>>> The Fora Project is coming...>>>> https://github.com/jeswin/fora>>>> _______________________________________________>>>> es-discuss mailing list>>>> es-discuss at mozilla.org>>>> https://mail.mozilla.org/listinfo/es-discuss>>>> _______________________________________________>>>> es-discuss mailing list>>>> es-discuss at mozilla.org>>>> https://mail.mozilla.org/listinfo/es-discuss>>>>>>>>>>>>> _______________________________________________>>> es-discuss mailing list>>> es-discuss at mozilla.org>>> https://mail.mozilla.org/listinfo/es-discuss>>>>>>>>>> _______________________________________________>> es-discuss mailing list>> es-discuss at mozilla.org>> https://mail.mozilla.org/listinfo/es-discuss>>>>>>> --> Cheers,> --MarkM>-------------- next part --------------An HTML attachment was scrubbed...URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140911/1e68d8c7/attachment.html>Furthermore, since await is call graph infectious, for those really wantingto use it, it means that before long, await is written before every singlefunction call. Which makes no sense.
Furthermore, since await is call graph infectious, for those really wantingto use it, it means that before long, await is written before every singlefunction call. Which makes no sense.On Thu, Sep 11, 2014 at 4:22 PM, Florian Bösch <pyalot at gmail.com> wrote:> A -> B -> C -> D -> E changes to>> A -> B -> C -> D -> async E and causes>> A await -> B await -> C await -> D await -> async E>> And of course if A, B, C or D is used anywhere else it percolates trough> the entire call graph.>> Trying to protect people from interlaved code execution effects is noble.> But doing so by introducing a rote thing to type everytime you change the> code somewhere underneath is wrong. It's wrong because it breaks logic> isolation, it becomes impossible to change part of the library/utiity code> without this change affecting all code that uses it. This guarantees that> it doesn't happen in practice, because it's too painful to do. It requires> the code, that uses other code, to know about the internal behavior of that> code.>> If say, I'd propose a semantic that required you to write "foo" in the> code, but just for those pieces of code that contain mentions of "bar", or> that reference code that contains "bar" to the Nth degree, you'd accuse me> of trying to introduce a purposefully unusable feature. How is await/async> not an unusable feature?>>>> On Thu, Sep 11, 2014 at 4:00 PM, Mark S. Miller <erights at google.com>> wrote:>>> On Thu, Sep 11, 2014 at 6:20 AM, Florian Bösch <pyalot at gmail.com> wrote:>>>>> await has also another problem in that if somewhere, deep down the call>>> stack, something is intending to do async, then up the entire call stack>>> everywhere you've got to insert await. It's a bit of a headache for code>>> maintenance (hello bicycle repair man jam session), and it's also fairly>>> unfriendly for library authors.>>>>>> There is a solution to that problem, which is not using generators if>>> you'd want co-routines. If you want co-routine like behavior, please>>> implement co-routines (and you can prop whatever scheduling/managing on top>>> of that).>>>>>> An argument has been made in earlier discussions on that topic, that JS>>> VMs can't deal with co-routines (garbage collection, DOM, whatever). But>>> surely, if the VM can support generators/continuations, it could support>>> full co-routines.>>>>>>> VM issues are not the argument against coroutines or deep generators. The>> issue is that unpredictable interleaving makes reasoning about invariants>> much too difficult. Without these, we have an important guarantee: When f>> synchronously calls g, the only side effects that might have occurred by>> the time g returns to f are those g might have caused. Thus, these are the>> only side effect possibilities that f must worry about.>>>> See section 18.3 of <http://www.erights.org/talks/thesis/markm-thesis.pdf>>> and replace postfix diagonal uparrow with prefix "await". In ES7 the>> example would be>>>> async function foo() { return bar(await getint(), y()); }>> ... await foo() ...>>>> The net effect is like co-routines, except that the placement of "async">> and "await" -- like the diagonal uparrow in the text -- marks the places>> where interleaving might occur. This is as close to coroutine support as we>> should ever come.>>>>>>>>> I'd recommend python's greenlet as an outstanding implementation of a>>> co-routine interface that includes basically everything one could wish from>>> it. I'd not consider python 3's "asyncio" a proper co-routine>>> implementation (i.e. it's the same generator/await hack as is being>>> discussed here).>>>>>> On Thu, Sep 11, 2014 at 3:05 PM, Kevin Smith <zenparsing at gmail.com>>>> wrote:>>>>>>> Also, see https://github.com/lukehoban/ecmascript-asyncawait/issues/14>>>> for previous discussion.>>>>>>>> On Thu, Sep 11, 2014 at 8:42 AM, Domenic Denicola <>>>> domenic at domenicdenicola.com> wrote:>>>>>>>>> There are several problems solved by async/await instead of twisting>>>>> generators:>>>>>>>>>> 1. What if you wanted to use generators for lazy sequences>>>>> (iterables), instead of asynchronicity? If your framework assumes all>>>>> generators are for async, you lose the original use case of generators.>>>>>>>>>> 2. Say what you mean. `function*` and `yield` mean something very>>>>> different from `async function` and `await`, similar to how>>>>> `Subclass.prototype = Object.create(Superclass.prototype);>>>>> Subclass.prototype.constructor = Subclass` is different from `class>>>>> Subclass extends Superclass`.>>>>>>>>>> 3. Operator precedence. You can do `await a + await b` to mean `(await>>>>> a) + (await b)`, but `yield a + yield b` means `yield (a + (yield b))`.>>>>>>>>>> 4. Ability to produce promise-returning functions without buying into>>>>> a specific framework that interprets generators in a certain way. E.g., you>>>>> could use `async function f() { return 5; }` to return a promise for 5,>>>>> which people can consume with `f().then(v => ...)`. If you try to do>>>>> `function* f() { return 5; }` you will get an iterable, which is not>>>>> understood to be asynchronous. (Hopefully my use of `return 5` for brevity>>>>> instead of more complex code does not confuse this point for you.)>>>>>>>>>> As for stack traces, long stack trace support is a debugging feature,>>>>> and the fact that `yield*` gets them right now doesn't mean that `await`>>>>> won't get them in the future.>>>>>>>>>> -----Original Message----->>>>> From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of>>>>> Jeswin Kumar>>>>> Sent: Thursday, September 11, 2014 11:46>>>>> To: es-discuss at mozilla.org>>>>> Subject: Does async/await solve a real problem?>>>>>>>>>> Looking at my project (in which asynchronous calls are entirely done>>>>> via generators), I can't see how async/await would simplify code for>>>>> end-users like me (application programmers).>>>>>>>>>> End users write the spawn()/Q.async()/co() wrapper *at most* one>>>>> single time in an application:>>>>> 1. When using a framework like say koajs, you don't have to write it>>>>> even once.>>>>> 2. While not using a framework, you'd have to use the wrapper one>>>>> single time in say, the main.js file.>>>>>>>>>> To use the example at>>>>> http://wiki.ecmascript.org/doku.php?id=strawman:async_functions>>>>>>>>>> async function chainAnimationsAsync(elem, animations) { CODE; } is>>>>> just function chainAnimationsAsync*(elem, animations) { same CODE; } when>>>>> flow control is done by a framework or at the entry point to your>>>>> application. spawn() isn't needed.>>>>>>>>>> I can't see how this will reduce application's code even a little. So>>>>> my question is, is async/await needed?>>>>>>>>>>>>>>> One more question>>>>> -------------------------->>>>> 1. yield is practically very difficult to use in a project because you>>>>> don't get proper stack traces (at least with the current flow control>>>>> libraries). You'd only see the last call which threw the error, and then>>>>> functions from the flow control library immediately below that. I suppose>>>>> the generators leading up to the erring generator are all suspended and>>>>> wouldn't be on the stack frame chain.>>>>>>>>>> 2. yield* generator delegation solves this problem, you get real stack>>>>> traces. I was able to get full stack traces simply by replacing all yield>>>>> X() with yield* X()>>>>>>>>>> example code as in: https://github.com/chopachom/syncio>>>>>>>>>> So if there are valid use-cases for adding async/await to JS,>>>>> shouldn't it be based on how yield* works rather than yield?>>>>>>>>>> -- Jes>>>>>>>>>> The Fora Project is coming...>>>>> https://github.com/jeswin/fora>>>>> _______________________________________________>>>>> es-discuss mailing list>>>>> es-discuss at mozilla.org>>>>> https://mail.mozilla.org/listinfo/es-discuss>>>>> _______________________________________________>>>>> es-discuss mailing list>>>>> es-discuss at mozilla.org>>>>> https://mail.mozilla.org/listinfo/es-discuss>>>>>>>>>>>>>>>>> _______________________________________________>>>> es-discuss mailing list>>>> es-discuss at mozilla.org>>>> https://mail.mozilla.org/listinfo/es-discuss>>>>>>>>>>>>>> _______________________________________________>>> es-discuss mailing list>>> es-discuss at mozilla.org>>> https://mail.mozilla.org/listinfo/es-discuss>>>>>>>>>>>> -->> Cheers,>> --MarkM>>>>-------------- next part --------------An HTML attachment was scrubbed...URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140911/4a4e2a30/attachment-0001.html>On Thu, Sep 11, 2014 at 7:22 AM, Florian Bösch <pyalot atgmail.com> wrote:
A -> B -> C -> D -> E changes to
A -> B -> C -> D -> async E and causes
A await -> B await -> C await -> D await -> async E
And of course if A, B, C or D is used anywhere else it percolates troughthe entire call graph.
Trying to protect people from interlaved code execution effects is noble.
Exactly. Good to see we agree on the implications even if we value theseoutcomes differently.
But doing so by introducing a rote thing to type everytime you change thecode somewhere underneath is wrong. It's wrong because it breaks logicisolation, it becomes impossible to change part of the library/utiity codewithout this change affecting all code that uses it. This guarantees thatit doesn't happen in practice, because it's too painful to do. It requiresthe code, that uses other code, to know about the internal behavior of thatcode.
If say, I'd propose a semantic that required you to write "foo" in thecode, but just for those pieces of code that contain mentions of "bar", orthat reference code that contains "bar" to the Nth degree, you'd accuse meof trying to introduce a purposefully unusable feature. How is await/asyncnot an unusable feature?
On Thu, Sep 11, 2014 at 7:25 AM, Florian Bösch <pyalot atgmail.com> wrote:
Furthermore, since await is call graph infectious, for those reallywanting to use it, it means that before long, await is written before everysingle function call. Which makes no sense.
In a purely functional system without side effects of any form (i.e.,ignoring even strictness and exceptions), interleaving is harmless, so thisconclusion is valid. You would indeed safely get better code reuse if youplaced async/await everywhere, so that instead should have been the defaultin such a language. In fact, since interleaving is harmless, you canschedule all computation as you like, including in parallel, lazily,eagerly, whatever, without harming correctness. In the absence of sideeffects, we should indeed not have placed a notational burden oninterleaving points.
But in an imperative system with synchronous side effects that co-existswith interleaving hazards, we also needs a way to protect against suchhazards. The economy of the communicating event loop model is that eachturn is implicitly a mutually exclusive atomic transaction -- withoutneeding extra notation for synchronization or transaction boundaries.
The cost of making atomicity cheap is that interleaving points must be madeexplicit. With callbacks, this cost is quite high. Promises reduce thiscost substantially. async/await further reduces this cost about as far asit can be reduced, while still leaving an explicit marker.
On Thu, Sep 11, 2014 at 7:22 AM, Florian Bösch <pyalot at gmail.com> wrote:> A -> B -> C -> D -> E changes to>> A -> B -> C -> D -> async E and causes>> A await -> B await -> C await -> D await -> async E>> And of course if A, B, C or D is used anywhere else it percolates trough> the entire call graph.>> Trying to protect people from interlaved code execution effects is noble.>Exactly. Good to see we agree on the implications even if we value theseoutcomes differently.> But doing so by introducing a rote thing to type everytime you change the> code somewhere underneath is wrong. It's wrong because it breaks logic> isolation, it becomes impossible to change part of the library/utiity code> without this change affecting all code that uses it. This guarantees that> it doesn't happen in practice, because it's too painful to do. It requires> the code, that uses other code, to know about the internal behavior of that> code.>> If say, I'd propose a semantic that required you to write "foo" in the> code, but just for those pieces of code that contain mentions of "bar", or> that reference code that contains "bar" to the Nth degree, you'd accuse me> of trying to introduce a purposefully unusable feature. How is await/async> not an unusable feature?>On Thu, Sep 11, 2014 at 7:25 AM, Florian Bösch <pyalot at gmail.com> wrote:> Furthermore, since await is call graph infectious, for those really> wanting to use it, it means that before long, await is written before every> single function call. Which makes no sense.>In a purely functional system without side effects of any form (i.e.,ignoring even strictness and exceptions), interleaving is harmless, so thisconclusion is valid. You would indeed safely get better code reuse if youplaced async/await everywhere, so that instead should have been the defaultin such a language. In fact, since interleaving is harmless, you canschedule all computation as you like, including in parallel, lazily,eagerly, whatever, without harming correctness. In the absence of sideeffects, we should indeed not have placed a notational burden oninterleaving points.But in an imperative system with synchronous side effects that co-existswith interleaving hazards, we also needs a way to protect against suchhazards. The economy of the communicating event loop model is that eachturn is implicitly a mutually exclusive atomic transaction -- withoutneeding extra notation for synchronization or transaction boundaries.The cost of making atomicity cheap is that interleaving points must be madeexplicit. With callbacks, this cost is quite high. Promises reduce thiscost substantially. async/await further reduces this cost about as far asit can be reduced, while still leaving an explicit marker.-- Cheers, --MarkM-------------- next part --------------An HTML attachment was scrubbed...URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140911/028c1af2/attachment.html>
The problem of code interleaving isn't on a fundamental level (like withthreads). Threads are a very different best, that interlave at randompoints in time, and hence require some heavy lifting by the environmnet/OSto not immediately fall flat.
Cooperative multitasking between I/O primitives is much friendlier. Butstill, you can wreck some havoc if you have multiple tasklets running thatmodify the same data structure. You won't get a race condition in theclassical sense, but you can still produce garbage data (like say call outto async inside a loop over an array of which you've cached the length).
However, the exact same breakage applies to await/async, because if youawait inside a loop, of which you've cached the length, and have some othercode modify the array in the meantime... So not really an argument.
The problem of code interleaving isn't on a fundamental level (like withthreads). Threads are a very different best, that interlave at randompoints in time, and hence require some heavy lifting by the environmnet/OSto not immediately fall flat.Cooperative multitasking between I/O primitives is much friendlier. Butstill, you can wreck some havoc if you have multiple tasklets running thatmodify the same data structure. You won't get a race condition in theclassical sense, but you can still produce garbage data (like say call outto async inside a loop over an array of which you've cached the length).However, the exact same breakage applies to await/async, because if youawait inside a loop, of which you've cached the length, and have some othercode modify the array in the meantime... So not really an argument.On Thu, Sep 11, 2014 at 4:55 PM, Mark S. Miller <erights at google.com> wrote:> On Thu, Sep 11, 2014 at 7:22 AM, Florian Bösch <pyalot at gmail.com> wrote:>>> A -> B -> C -> D -> E changes to>>>> A -> B -> C -> D -> async E and causes>>>> A await -> B await -> C await -> D await -> async E>>>> And of course if A, B, C or D is used anywhere else it percolates trough>> the entire call graph.>>>> Trying to protect people from interlaved code execution effects is noble.>>>> Exactly. Good to see we agree on the implications even if we value these> outcomes differently.>>>>> But doing so by introducing a rote thing to type everytime you change the>> code somewhere underneath is wrong. It's wrong because it breaks logic>> isolation, it becomes impossible to change part of the library/utiity code>> without this change affecting all code that uses it. This guarantees that>> it doesn't happen in practice, because it's too painful to do. It requires>> the code, that uses other code, to know about the internal behavior of that>> code.>>>> If say, I'd propose a semantic that required you to write "foo" in the>> code, but just for those pieces of code that contain mentions of "bar", or>> that reference code that contains "bar" to the Nth degree, you'd accuse me>> of trying to introduce a purposefully unusable feature. How is await/async>> not an unusable feature?>>>> On Thu, Sep 11, 2014 at 7:25 AM, Florian Bösch <pyalot at gmail.com> wrote:>>> Furthermore, since await is call graph infectious, for those really>> wanting to use it, it means that before long, await is written before every>> single function call. Which makes no sense.>>>> In a purely functional system without side effects of any form (i.e.,> ignoring even strictness and exceptions), interleaving is harmless, so this> conclusion is valid. You would indeed safely get better code reuse if you> placed async/await everywhere, so that instead should have been the default> in such a language. In fact, since interleaving is harmless, you can> schedule all computation as you like, including in parallel, lazily,> eagerly, whatever, without harming correctness. In the absence of side> effects, we should indeed not have placed a notational burden on> interleaving points.>> But in an imperative system with synchronous side effects that co-exists> with interleaving hazards, we also needs a way to protect against such> hazards. The economy of the communicating event loop model is that each> turn is implicitly a mutually exclusive atomic transaction -- without> needing extra notation for synchronization or transaction boundaries.>> The cost of making atomicity cheap is that interleaving points must be> made explicit. With callbacks, this cost is quite high. Promises reduce> this cost substantially. async/await further reduces this cost about as far> as it can be reduced, while still leaving an explicit marker.>> --> Cheers,> --MarkM>-------------- next part --------------An HTML attachment was scrubbed...URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140911/58523665/attachment-0001.html>
On 9/11/14, 10:22 AM, Florian Bösch wrote:
A -> B -> C -> D -> E changes to
A -> B -> C -> D -> async E and causes
A await -> B await -> C await -> D await -> async E
And of course if A, B, C or D is used anywhere else it percolatestrough the entire call graph.
Sort of true, but this is no worse than the status quo.
functionA(){return B(); }functionB(){return C(); }functionD(){return D(); }functionE(){return42; } ->functionE(){returnnewPromise(function(res, rej){ res(42); }); }I said "sort of" above because it's also worth pointing out that, insome cases, outer plain functions can still consume async functionswithout needing to themselves become async. The case arises where theresult of the outer plain function doesn't depend on the async result ofthe inner function. Many cases don't apply here, but it's worth pointingout to show that consumption of async functions isn'talways stack-viral:
functionadd(a, b){var logSuccess = log('adding ' + a +' and ' + b);if (!logSucces) {console.warn('log failed!'); }return a + b;}functionlog(msg){return logger.syncAppend(msg);}changes to
functionadd(a, b){ log('adding ' + a +' and ' + b).catch(function(res){console.warn('log failed!'); }).done();return a + b;}asyncfunctionlog(msg){return logger.asyncAppend(msg);}On 9/11/14, 10:22 AM, Florian Bösch wrote:> A -> B -> C -> D -> E changes to>> A -> B -> C -> D -> async E and causes>> A await -> B await -> C await -> D await -> async E>> And of course if A, B, C or D is used anywhere else it percolates > trough the entire call graph.Sort of true, but this is no worse than the status quo.function A() { return B(); }function B() { return C(); }function D() { return D(); }function E() { return 42; } -> function E() { return new Promise(function(res, rej) { res(42); }); }I said "sort of" above because it's also worth pointing out that, in some cases, outer plain functions can still consume async functions without needing to themselves become async. The case arises where the result of the outer plain function doesn't depend on the async result of the inner function. Many cases don't apply here, but it's worth pointing out to show that consumption of async functions isn't *always* stack-viral:function add(a, b) { var logSuccess = log('adding ' + a + ' and ' + b); if (!logSucces) { console.warn('log failed!'); } return a + b;}function log(msg) { return logger.syncAppend(msg);}changes tofunction add(a, b) { log('adding ' + a + ' and ' + b).catch(function(res) { console.warn('log failed!'); }).done(); return a + b;}async function log(msg) { return logger.asyncAppend(msg);}-Jeff>> Trying to protect people from interlaved code execution effects is > noble. But doing so by introducing a rote thing to type everytime you > change the code somewhere underneath is wrong. It's wrong because it > breaks logic isolation, it becomes impossible to change part of the > library/utiity code without this change affecting all code that uses > it. This guarantees that it doesn't happen in practice, because it's > too painful to do. It requires the code, that uses other code, to know > about the internal behavior of that code.>> If say, I'd propose a semantic that required you to write "foo" in the > code, but just for those pieces of code that contain mentions of > "bar", or that reference code that contains "bar" to the Nth degree, > you'd accuse me of trying to introduce a purposefully unusable > feature. How is await/async not an unusable feature?>>>> On Thu, Sep 11, 2014 at 4:00 PM, Mark S. Miller <erights at google.com > <mailto:erights at google.com>> wrote:>> On Thu, Sep 11, 2014 at 6:20 AM, Florian Bösch <pyalot at gmail.com> <mailto:pyalot at gmail.com>> wrote:>> await has also another problem in that if somewhere, deep down> the call stack, something is intending to do async, then up> the entire call stack everywhere you've got to insert await.> It's a bit of a headache for code maintenance (hello bicycle> repair man jam session), and it's also fairly unfriendly for> library authors.>> There is a solution to that problem, which is not using> generators if you'd want co-routines. If you want co-routine> like behavior, please implement co-routines (and you can prop> whatever scheduling/managing on top of that).>> An argument has been made in earlier discussions on that> topic, that JS VMs can't deal with co-routines (garbage> collection, DOM, whatever). But surely, if the VM can support> generators/continuations, it could support full co-routines.>>> VM issues are not the argument against coroutines or deep> generators. The issue is that unpredictable interleaving makes> reasoning about invariants much too difficult. Without these, we> have an important guarantee: When f synchronously calls g, the> only side effects that might have occurred by the time g returns> to f are those g might have caused. Thus, these are the only side> effect possibilities that f must worry about.>> See section 18.3 of> <http://www.erights.org/talks/thesis/markm-thesis.pdf> and replace> postfix diagonal uparrow with prefix "await". In ES7 the example> would be>> async function foo() { return bar(await getint(), y()); }> ... await foo() ...>> The net effect is like co-routines, except that the placement of> "async" and "await" -- like the diagonal uparrow in the text --> marks the places where interleaving might occur. This is as close> to coroutine support as we should ever come.>>>> I'd recommend python's greenlet as an outstanding> implementation of a co-routine interface that includes> basically everything one could wish from it. I'd not consider> python 3's "asyncio" a proper co-routine implementation (i.e.> it's the same generator/await hack as is being discussed here).>> On Thu, Sep 11, 2014 at 3:05 PM, Kevin Smith> <zenparsing at gmail.com <mailto:zenparsing at gmail.com>> wrote:>> Also, see> https://github.com/lukehoban/ecmascript-asyncawait/issues/14> for previous discussion.>> On Thu, Sep 11, 2014 at 8:42 AM, Domenic Denicola> <domenic at domenicdenicola.com> <mailto:domenic at domenicdenicola.com>> wrote:>> There are several problems solved by async/await> instead of twisting generators:>> 1. What if you wanted to use generators for lazy> sequences (iterables), instead of asynchronicity? If> your framework assumes all generators are for async,> you lose the original use case of generators.>> 2. Say what you mean. `function*` and `yield` mean> something very different from `async function` and> `await`, similar to how `Subclass.prototype => Object.create(Superclass.prototype);> Subclass.prototype.constructor = Subclass` is> different from `class Subclass extends Superclass`.>> 3. Operator precedence. You can do `await a + await b`> to mean `(await a) + (await b)`, but `yield a + yield> b` means `yield (a + (yield b))`.>> 4. Ability to produce promise-returning functions> without buying into a specific framework that> interprets generators in a certain way. E.g., you> could use `async function f() { return 5; }` to return> a promise for 5, which people can consume with> `f().then(v => ...)`. If you try to do `function* f()> { return 5; }` you will get an iterable, which is not> understood to be asynchronous. (Hopefully my use of> `return 5` for brevity instead of more complex code> does not confuse this point for you.)>> As for stack traces, long stack trace support is a> debugging feature, and the fact that `yield*` gets> them right now doesn't mean that `await` won't get> them in the future.>> -----Original Message-----> From: es-discuss> [mailto:es-discuss-bounces at mozilla.org> <mailto:es-discuss-bounces at mozilla.org>] On Behalf Of> Jeswin Kumar> Sent: Thursday, September 11, 2014 11:46> To: es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>> Subject: Does async/await solve a real problem?>> Looking at my project (in which asynchronous calls are> entirely done via generators), I can't see how> async/await would simplify code for end-users like me> (application programmers).>> End users write the spawn()/Q.async()/co() wrapper *at> most* one single time in an application:> 1. When using a framework like say koajs, you don't> have to write it even once.> 2. While not using a framework, you'd have to use the> wrapper one single time in say, the main.js file.>> To use the example at> http://wiki.ecmascript.org/doku.php?id=strawman:async_functions>> async function chainAnimationsAsync(elem, animations)> { CODE; } is just function chainAnimationsAsync*(elem,> animations) { same CODE; } when flow control is done> by a framework or at the entry point to your> application. spawn() isn't needed.>> I can't see how this will reduce application's code> even a little. So my question is, is async/await needed?>>> One more question> --------------------------> 1. yield is practically very difficult to use in a> project because you don't get proper stack traces (at> least with the current flow control libraries). You'd> only see the last call which threw the error, and then> functions from the flow control library immediately> below that. I suppose the generators leading up to the> erring generator are all suspended and wouldn't be on> the stack frame chain.>> 2. yield* generator delegation solves this problem,> you get real stack traces. I was able to get full> stack traces simply by replacing all yield X() with> yield* X()>> example code as in: https://github.com/chopachom/syncio>> So if there are valid use-cases for adding async/await> to JS, shouldn't it be based on how yield* works> rather than yield?>> -- Jes>> The Fora Project is coming...> https://github.com/jeswin/fora> _______________________________________________> es-discuss mailing list> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>> https://mail.mozilla.org/listinfo/es-discuss> _______________________________________________> es-discuss mailing list> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>> https://mail.mozilla.org/listinfo/es-discuss>>>> _______________________________________________> es-discuss mailing list> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>> https://mail.mozilla.org/listinfo/es-discuss>>>> _______________________________________________> es-discuss mailing list> es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>> https://mail.mozilla.org/listinfo/es-discuss>>>>> -- > Cheers,> --MarkM>>>>> _______________________________________________> es-discuss mailing list> es-discuss at mozilla.org> https://mail.mozilla.org/listinfo/es-discuss-------------- next part --------------An HTML attachment was scrubbed...URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140911/2b5ef4c0/attachment-0001.html>I'd like to inject my own experience into this discussion.
I am the author of streamline.js, a smalll extension to JavaScript whichadds async/await semantics to the language. I developed this languageextension in January 2011 and we have been using it since then in a largeproject (new web stack for a commercial ERP product). Before, in the earlydays of the project, we had been using raw callbacks and promise libraries.
The syntax of streamline.js is different from async/await but the semanticsare aligned. The marker is different (_ instead of await) but it isexplicit at all levels. Parallelism is achieved with futures, which arejust simplified promises (the language was made promise friendly recently).
My experience is that async/await makes a huge difference for largeprojects with a lot of logic sitting on top of async APIs. Morespecifically:
So, from my experience, async/await is an absolute must for projects likeours.
On the syntax side, I am not convinced by the await construct. The problemis that await is prefix. So it does not play well in chains of async calls.Consider the following:
I'm not particularly attached to the streamline syntax (reserving anidentifier was an easy way to remain compatible with existing languagetools: editors, linters, syntax coloring, ...). I would rather go withsomething like the infix ! proposal ofstrawman:concurrency.
I think that Mark Miller nails it really well:
The cost of making atomicity cheap is that interleaving points must bemade explicit. With callbacks, this cost is quite high. Promises reducethis cost substantially. async/await further reduces this cost about as faras it can be reduced, while still leaving an explicit marker.
IMO promises are only a step forwards; async/await is a leap forwards.
Bruno
I'd like to inject my own experience into this discussion.I am the author of streamline.js, a smalll extension to JavaScript whichadds async/await semantics to the language. I developed this languageextension in January 2011 and we have been using it since then in a largeproject (new web stack for a commercial ERP product). Before, in the earlydays of the project, we had been using raw callbacks and promise libraries.The syntax of streamline.js is different from async/await but the semanticsare aligned. The marker is different (_ instead of await) but it isexplicit at all levels. Parallelism is achieved with futures, which arejust simplified promises (the language was made promise friendly recently).My experience is that async/await makes a huge difference for largeprojects with a lot of logic sitting on top of async APIs. Morespecifically: - Productivity: developers write code faster and they write less code. - Maintainability: code is easier to read and understand. Control flow is not disrupted by callbacks. Refactoring and modification are easy. - Robustness: Error handling, resource cleanup and invariant restoring can be done in a simple and reliable way with.well known constructs (try/catch and try/finally). Developers don't make silly mistakes like forgetting a callback or calling it twice. - Learnability: developers coming from other languages (Java, C#) can be brought up to speed quickly. - Comprehensive parallelization: async/await blends well with futures/promises. Concurrency problems are easy to analyze because control flow is natural and the markers highlight all the points where the flow may yield control. - TLS support: makes it easy to maintain a localization/security context across async calls.So, from my experience, async/await is an absolute must for projects likeours.On the syntax side, I am not convinced by the await construct. The problemis that await is prefix. So it does not play well in chains of async calls.Consider the following: - streamline: f1(_).f2(_).f3(_) - async/await: await (await (await f1()).f2()).f3();I'm not particularly attached to the streamline syntax (reserving anidentifier was an easy way to remain compatible with existing languagetools: editors, linters, syntax coloring, ...). I would rather go withsomething like the infix ! proposal ofhttp://wiki.ecmascript.org/doku.php?id=strawman:concurrency.I think that Mark Miller nails it really well:> The cost of making atomicity cheap is that interleaving points must be> made explicit. With callbacks, this cost is quite high. Promises reduce> this cost substantially. async/await further reduces this cost about as far> as it can be reduced, while still leaving an explicit marker.>>IMO promises are only a step forwards; async/await is a leap forwards.Bruno-------------- next part --------------An HTML attachment was scrubbed...URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140911/38cd9f12/attachment.html>
On Thu, Sep 11, 2014 at 4:41 PM, Bruno Jouhier <bjouhier atgmail.com> wrote:
I think that Mark Miller nails it really well:
The cost of making atomicity cheap is that interleaving points must bemade explicit. With callbacks, this cost is quite high. Promises reduce thiscost substantially. async/await further reduces this cost about as far as itcan be reduced, while still leaving an explicit marker.
IMO promises are only a step forwards; async/await is a leap forwards.
And, at the risk of banging my own drum -- async/await pave the wayfor some really interesting work with optimistic transactional lockingand speculative execution, since they are (as noted in this thread)explicit markers of atomicity boundaries.
On Thu, Sep 11, 2014 at 4:41 PM, Bruno Jouhier <bjouhier at gmail.com> wrote:> I think that Mark Miller nails it really well:>>>> The cost of making atomicity cheap is that interleaving points must be>> made explicit. With callbacks, this cost is quite high. Promises reduce this>> cost substantially. async/await further reduces this cost about as far as it>> can be reduced, while still leaving an explicit marker.>> IMO promises are only a step forwards; async/await is a leap forwards.And, at the risk of banging my own drum -- async/await pave the wayfor some really interesting work with optimistic transactional lockingand speculative execution, since they are (as noted in this thread)explicit markers of atomicity boundaries. --scott
I forgot to mention performance.
streamline has been an interesting playground to test performance becauseit supports 3 different backends:
Pure callbacks are the fastest when the execution stacks are shallow andthe logic is simple.
When the call stacks get deep and/or the logic gets complex, fibers win.This is because we have several layers of async functions calling asyncfunctions before we hit low level callback APIs. The intermediate asynccalls are executed as normal calls with normal returns (no callback, noallocation of a generator object) until we reach the low level callbackAPIs (where a Yield pops up). With some code patterns like caching (whereonly the first call is truly async) the fibers backend can be order ofmagnitudes faster than callbacks (even handcrafted ones).
Generators always lag behind (but they have more regular memory patternsthan fibers). Part of this poor performance may also be due to lack ofoptimization in V8.
If async/await is baked into the language VM implementers will be able tochoose between different strategies: callbacks, single frame continuations,deep continuations, ... to optimize the code. The nice thing is that theawait markers will always be there on the surface, even if the VM takessome shortcuts below.
Bruno
2014-09-11 22:48 GMT+02:00 C. Scott Ananian <ecmascript atcscott.net>:
I forgot to mention performance.streamline has been an interesting playground to test performance becauseit supports 3 different backends: - pure callbacks with a sophisticated CPS transform - fibers (much simpler transform) - generators (transform is similar to fibers, runtime is a bit trickier).Pure callbacks are the fastest when the execution stacks are shallow andthe logic is simple.When the call stacks get deep and/or the logic gets complex, fibers win.This is because we have several layers of async functions calling asyncfunctions before we hit low level callback APIs. The intermediate asynccalls are executed as normal calls with normal returns (no callback, noallocation of a generator object) until we reach the low level callbackAPIs (where a Yield pops up). With some code patterns like caching (whereonly the first call is truly async) the fibers backend can be order ofmagnitudes faster than callbacks (even handcrafted ones).Generators always lag behind (but they have more regular memory patternsthan fibers). Part of this poor performance may also be due to lack ofoptimization in V8.If async/await is baked into the language VM implementers will be able tochoose between different strategies: callbacks, single frame continuations,deep continuations, ... to optimize the code. The nice thing is that theawait markers will always be there on the surface, even if the VM takessome shortcuts below.Bruno2014-09-11 22:48 GMT+02:00 C. Scott Ananian <ecmascript at cscott.net>:> On Thu, Sep 11, 2014 at 4:41 PM, Bruno Jouhier <bjouhier at gmail.com> wrote:> > I think that Mark Miller nails it really well:> >>> >> The cost of making atomicity cheap is that interleaving points must be> >> made explicit. With callbacks, this cost is quite high. Promises reduce> this> >> cost substantially. async/await further reduces this cost about as far> as it> >> can be reduced, while still leaving an explicit marker.> >> > IMO promises are only a step forwards; async/await is a leap forwards.>> And, at the risk of banging my own drum -- async/await pave the way> for some really interesting work with optimistic transactional locking> and speculative execution, since they are (as noted in this thread)> explicit markers of atomicity boundaries.> --scott>-------------- next part --------------An HTML attachment was scrubbed...URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140911/c7b595ed/attachment-0001.html>
On Thu, Sep 11, 2014 at 5:27 PM, Bruno Jouhier <bjouhier atgmail.com> wrote:
If async/await is baked into the language VM implementers will be able tochoose between different strategies: callbacks, single frame continuations,deep continuations, ...
...and transactional speculation. Or at least, that was the project Iwas hacking on in Rust/Turtlescript for a while(cscott/rusty-turtle) when I was fishing for a jobat Mozilla.
The idea is that you actually execute all ready-to-execute tasks in(true multithreaded, multiprocessor) parallel, relying on theprogrammer's explicit atomicity boundaries and transactional memory toclean things up in order to preserve the appearance of atomic in-orderexecution.
On Thu, Sep 11, 2014 at 5:27 PM, Bruno Jouhier <bjouhier at gmail.com> wrote:> If async/await is baked into the language VM implementers will be able to> choose between different strategies: callbacks, single frame continuations,> deep continuations, ......and transactional speculation. Or at least, that was the project Iwas hacking on in Rust/Turtlescript for a while(https://github.com/cscott/rusty-turtle) when I was fishing for a jobat Mozilla.The idea is that you actually execute all ready-to-execute tasks in(true multithreaded, multiprocessor) parallel, relying on theprogrammer's explicit atomicity boundaries and transactional memory toclean things up in order to preserve the appearance of atomic in-orderexecution. --scott
2014-09-11 16:22 GMT+02:00 Florian Bösch <pyalot atgmail.com>:
A -> B -> C -> D -> E changes to
A -> B -> C -> D -> async E and causes
A await -> B await -> C await -> D await -> async E
And of course if A, B, C or D is used anywhere else it percolates troughthe entire call graph.
Trying to protect people from interlaved code execution effects is noble.But doing so by introducing a rote thing to type everytime you change thecode somewhere underneath is wrong. It's wrong because it breaks logicisolation, it becomes impossible to change part of the library/utiity codewithout this change affecting all code that uses it. This guarantees thatit doesn't happen in practice, because it's too painful to do. It requiresthe code, that uses other code, to know about the internal behavior of thatcode.
Arguably, the fact that a function may suspend in mid-flight should be partof its documentation (i.e. function signature), as it directly affects thecaller.
As an analogy, considerIO in Haskell: if you have this large piece ofpurely functional code, and deep down you're all of a sudden introducingmutable state, you will need to refactor the larger piece of code to revealthat it in fact performs IO as well.
While this refactoring may be tedious (no disagreement there), it forcesyou to review all affected code, which is good, because the dependencies ofthat code may have changed (i.e. side-effect the client previously thoughtwould execute atomically may now no longer be atomic).
2014-09-11 16:22 GMT+02:00 Florian Bösch <pyalot at gmail.com>:> A -> B -> C -> D -> E changes to>> A -> B -> C -> D -> async E and causes>> A await -> B await -> C await -> D await -> async E>> And of course if A, B, C or D is used anywhere else it percolates trough> the entire call graph.>> Trying to protect people from interlaved code execution effects is noble.> But doing so by introducing a rote thing to type everytime you change the> code somewhere underneath is wrong. It's wrong because it breaks logic> isolation, it becomes impossible to change part of the library/utiity code> without this change affecting all code that uses it. This guarantees that> it doesn't happen in practice, because it's too painful to do. It requires> the code, that uses other code, to know about the internal behavior of that> code.>Arguably, the fact that a function may suspend in mid-flight should be partof its documentation (i.e. function signature), as it directly affects thecaller.As an analogy, consider `IO` in Haskell: if you have this large piece ofpurely functional code, and deep down you're all of a sudden introducingmutable state, you will need to refactor the larger piece of code to revealthat it in fact performs IO as well.While this refactoring may be tedious (no disagreement there), it forcesyou to review all affected code, which is good, because the dependencies ofthat code may have changed (i.e. side-effect the client previously thoughtwould execute atomically may now no longer be atomic).Cheers,Tom-------------- next part --------------An HTML attachment was scrubbed...URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140912/ff25ae79/attachment-0001.html>
On Fri, Sep 12, 2014 at 8:00 AM, Tom Van Cutsem <tomvc.be atgmail.com> wrote:
While this refactoring may be tedious (no disagreement there), it forcesyou to review all affected code, which is good, because the dependencies ofthat code may have changed (i.e. side-effect the client previously thoughtwould execute atomically may now no longer be atomic).
It's often the case that the code which uses the async code, uses othercode, which isn't authored by the author, and isn't documented for awaitcompatibility. For instance:
$.each(somelist, async function(item){await Foobar(item);});
Suppose $ is jQuery, is this async safe? Google for jQuery documentationand/or read code.
somelist.each(async function(item){await Foobar(item);});
Has the browser made his each function async safe? Google for JS APIdocumentation of the browser and/or start reading the source code of thebrowser (really?).
This is kind of a pervasive problem. It's not only that therefactoring/hunting for references is tedious. It's that sometimes, youmight not have the plain source (some proprietary/minified/obfuscatedlibrary anybody?), or that the implementation is quite complex, andfrankly, you just don't have the time to hunt around its millions of linesof code to figure out if it'll work.
Now to be fair, the same problem applies to co-routines as well. You can'tknow if a given piece of code is co-routine safe unless you dig it up. Butat least it doesn't make you type forwards/backwards await/async each timeyou decide it didn't work out, trough hundreds, or thousands, or maybe eventens of thousands of lines. In fact, doing this will become an utterimpossibility fairly quickly anyway, and the only way to do it would besome kind of automated tool that can insert the async/awaits for you troughthe code (pray that it's inferrable, or you're screwed). Ohyeah, it'd benice if that tool could insert some invisible code so you don't justclutter lines with stuff you don't hand-manage anyway. Wait, no, that wasco-routines again wasn't it? silly me.
On Fri, Sep 12, 2014 at 8:00 AM, Tom Van Cutsem <tomvc.be at gmail.com> wrote:>> While this refactoring may be tedious (no disagreement there), it forces> you to review all affected code, which is good, because the dependencies of> that code may have changed (i.e. side-effect the client previously thought> would execute atomically may now no longer be atomic).>It's often the case that the code which uses the async code, uses othercode, which isn't authored by the author, and isn't documented for awaitcompatibility. For instance:$.each(somelist, async function(item){ await Foobar(item);});Suppose $ is jQuery, is this async safe? Google for jQuery documentationand/or read code.somelist.each(async function(item){ await Foobar(item);});Has the browser made his each function async safe? Google for JS APIdocumentation of the browser and/or start reading the source code of thebrowser (really?).This is kind of a pervasive problem. It's not only that therefactoring/hunting for references is tedious. It's that sometimes, youmight not have the plain source (some proprietary/minified/obfuscatedlibrary anybody?), or that the implementation is quite complex, andfrankly, you just don't have the time to hunt around its millions of linesof code to figure out if it'll work.Now to be fair, the same problem applies to co-routines as well. You can'tknow if a given piece of code is co-routine safe unless you dig it up. Butat least it doesn't make you type forwards/backwards await/async each timeyou decide it didn't work out, trough hundreds, or thousands, or maybe eventens of thousands of lines. In fact, doing this will become an utterimpossibility fairly quickly anyway, and the only way to do it would besome kind of automated tool that can insert the async/awaits for you troughthe code (pray that it's inferrable, or you're screwed). Ohyeah, it'd benice if that tool could insert some invisible code so you don't justclutter lines with stuff you don't hand-manage anyway. Wait, no, that wasco-routines again wasn't it? silly me.-------------- next part --------------An HTML attachment was scrubbed...URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140912/24a3cf72/attachment.html>Arguably, the fact that a function may suspend in mid-flight should bepart of its documentation (i.e. function signature), as it directly affectsthe caller.
It does. With async/await the function signature must be marked with'async' if the function body contains 'await'.
As an analogy, consider
IOin Haskell: if you have this large piece ofpurely functional code, and deep down you're all of a sudden introducingmutable state, you will need to refactor the larger piece of code to revealthat it in fact performs IO as well.
While this refactoring may be tedious (no disagreement there), it forcesyou to review all affected code, which is good, because the dependencies ofthat code may have changed (i.e. side-effect the client previously thoughtwould execute atomically may now no longer be atomic).
It is tedious but not error prone. The compiler will tell you if you didanything wrong.
In a large project, high level code tends to be async anyway because thereis always some async code (from which you need a result) like a databaseoperation, a web service call, a file access, etc. underneath. Low levelcode is usually a mix of tight algorithms written in sync style and lowlevel I/O written in async style. So the refactoring, when it happens, isusually contained to one algorithm, as the high level code is alreadywritten in async style.
Refactoring sync code into async is completely straightforwards withasync/await. it is just a matter of adding async/await keywords. Withoutasync/await you have to restructure the code with callbacks or promiseswhich is a very different story: complex and error prone.
Also, an observation on explicit markers: these markers are very useful inlow level I/O handling code because they let you clearly see the boundariesof the atomic sequences. In high level code, they are less importantbecause the concurrency problems should be addressed at the low level andthe high level code usually deals with data which is specific to a requestand which is not shared with other requests.
When working with async/await you will also need a library function thatlets you protect critical sections containing await markers. In ourlibrary, we do it with a funnel:
var fun = createFunnel(1);
async function foo() {var zoo = await fun(async function() {// critical section containing await directives}}
Funnels can also be used to limit the level of concurrency (fun =flows.funnel(max)). This is a very simple way to avoid resource exhaustionin high concurrency scenarios.
>> Arguably, the fact that a function may suspend in mid-flight should be> part of its documentation (i.e. function signature), as it directly affects> the caller.>It does. With async/await the function signature must be marked with'async' if the function body contains 'await'.> As an analogy, consider `IO` in Haskell: if you have this large piece of> purely functional code, and deep down you're all of a sudden introducing> mutable state, you will need to refactor the larger piece of code to reveal> that it in fact performs IO as well.>> While this refactoring may be tedious (no disagreement there), it forces> you to review all affected code, which is good, because the dependencies of> that code may have changed (i.e. side-effect the client previously thought> would execute atomically may now no longer be atomic).>It is tedious but not error prone. The compiler will tell you if you didanything wrong.In a large project, high level code tends to be async anyway because thereis always some async code (from which you need a result) like a databaseoperation, a web service call, a file access, etc. underneath. Low levelcode is usually a mix of tight algorithms written in sync style and lowlevel I/O written in async style. So the refactoring, when it happens, isusually contained to one algorithm, as the high level code is alreadywritten in async style.Refactoring sync code into async is completely straightforwards withasync/await. it is just a matter of adding async/await keywords. Withoutasync/await you have to restructure the code with callbacks or promiseswhich is a very different story: complex and error prone.Also, an observation on explicit markers: these markers are very useful inlow level I/O handling code because they let you clearly see the boundariesof the atomic sequences. In high level code, they are less importantbecause the concurrency problems should be addressed at the low level andthe high level code usually deals with data which is specific to a requestand which is not shared with other requests.When working with async/await you will also need a library function thatlets you protect critical sections containing await markers. In ourlibrary, we do it with a funnel:var fun = createFunnel(1);async function foo() { var zoo = await fun(async function() { // critical section containing await directives }}Funnels can also be used to limit the level of concurrency (fun =flows.funnel(max)). This is a very simple way to avoid resource exhaustionin high concurrency scenarios.> Cheers,> Tom>> _______________________________________________> es-discuss mailing list> es-discuss at mozilla.org> https://mail.mozilla.org/listinfo/es-discuss>>-------------- next part --------------An HTML attachment was scrubbed...URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140912/e66858b4/attachment.html>On Fri, Sep 12, 2014 at 2:24 AM, Florian Bösch <pyalot atgmail.com> wrote:
It's often the case that the code which uses the async code, uses othercode, which isn't authored by the author, and isn't documented for awaitcompatibility. For instance:
$.each(somelist, async function(item){await Foobar(item);});
Suppose $ is jQuery, is this async safe? Google for jQuery documentationand/or read code.
I don't think this example is valid. As I understand it,async function is a function that returns a promise. You don't have tolook into the library at all, it's just a normal value-returningfunction from its perspective. I think your example should have beensomething like:
await Promise.all($.map(somelist, async function(item) { await Foobar(item); }).
But I'm not 100% up-to-speed on the await/async proposal. Hopefullysomeone will correct me if I've gotten this wrong. The bottom lineshould be that atomicity boundaries are explicitly noted withawait/async; for library code which does not expose an 'async' markeryou are guaranteed that it will execute atomically. In thisparticular example this just means that all the promises created bytheasync function will be created atomically and execution returnedto the calledbefore any of the bodies of theasync functions arerun.
somelist.each(async function(item){await Foobar(item);});
Has the browser made his each function async safe? Google for JS APIdocumentation of the browser and/or start reading the source code of thebrowser (really?).
This is exactly the same as the jquery example. Noimplementation-introspection needed.
On Fri, Sep 12, 2014 at 2:24 AM, Florian Bösch <pyalot at gmail.com> wrote:> It's often the case that the code which uses the async code, uses other> code, which isn't authored by the author, and isn't documented for await> compatibility. For instance:>> $.each(somelist, async function(item){> await Foobar(item);> });>> Suppose $ is jQuery, is this async safe? Google for jQuery documentation> and/or read code.>I don't think this example is valid. As I understand it, `asyncfunction` is a function that returns a promise. You don't have tolook into the library at all, it's just a normal value-returningfunction from its perspective. I think your example should have beensomething like:`await Promise.all($.map(somelist, async function(item) { awaitFoobar(item); })`.But I'm not 100% up-to-speed on the await/async proposal. Hopefullysomeone will correct me if I've gotten this wrong. The bottom lineshould be that atomicity boundaries are explicitly noted withawait/async; for library code which does not expose an 'async' markeryou are guaranteed that it will execute atomically. In thisparticular example this just means that all the promises created bythe `async function` will be created atomically and execution returnedto the called *before* any of the bodies of the `async function`s arerun.> somelist.each(async function(item){> await Foobar(item);> });>> Has the browser made his each function async safe? Google for JS API> documentation of the browser and/or start reading the source code of the> browser (really?).This is exactly the same as the jquery example. Noimplementation-introspection needed. --scottOn Fri, Sep 12, 2014 at 8:14 AM, C. Scott Ananian <ecmascript atcscott.net> wrote:
This is exactly the same as the jquery example. Noimplementation-introspection needed.
You've got it right.async function solely makes the functionreturn a Promise for its return value, rather than returning itsreturn value normally; nothing magical goes on beyond that. There'sabsolutely no need to check if a function is "async-safe" or propagateanasync on all functions up the call chain; you can just call thefunction normally and receive a Promise. You only need to makeyourselfasync if you want to consume the value of an async functioncall directly, without having to go through the Promise indirectionmanually.
Browsers will likely optimize cases where an async function call isimmediately consumed by an await expression and do some pipelining orsomething, but that's behind-the-scenes optimization.
On Fri, Sep 12, 2014 at 8:14 AM, C. Scott Ananian <ecmascript at cscott.net> wrote:> On Fri, Sep 12, 2014 at 2:24 AM, Florian Bösch <pyalot at gmail.com> wrote:>> It's often the case that the code which uses the async code, uses other>> code, which isn't authored by the author, and isn't documented for await>> compatibility. For instance:>>>> $.each(somelist, async function(item){>> await Foobar(item);>> });>>>> Suppose $ is jQuery, is this async safe? Google for jQuery documentation>> and/or read code.>>>> I don't think this example is valid. As I understand it, `async> function` is a function that returns a promise. You don't have to> look into the library at all, it's just a normal value-returning> function from its perspective. I think your example should have been> something like:>> `await Promise.all($.map(somelist, async function(item) { await> Foobar(item); })`.>> But I'm not 100% up-to-speed on the await/async proposal. Hopefully> someone will correct me if I've gotten this wrong. The bottom line> should be that atomicity boundaries are explicitly noted with> await/async; for library code which does not expose an 'async' marker> you are guaranteed that it will execute atomically. In this> particular example this just means that all the promises created by> the `async function` will be created atomically and execution returned> to the called *before* any of the bodies of the `async function`s are> run.>>> somelist.each(async function(item){>> await Foobar(item);>> });>>>> Has the browser made his each function async safe? Google for JS API>> documentation of the browser and/or start reading the source code of the>> browser (really?).>> This is exactly the same as the jquery example. No> implementation-introspection needed.You've got it right. `async function` solely makes the functionreturn a Promise for its return value, rather than returning itsreturn value normally; nothing magical goes on beyond that. There'sabsolutely no need to check if a function is "async-safe" or propagatean `async` on all functions up the call chain; you can just call thefunction normally and receive a Promise. You only need to makeyourself `async` if you want to consume the value of an async functioncall directly, without having to go through the Promise indirectionmanually.Browsers will likely optimize cases where an async function call isimmediately consumed by an await expression and do some pipelining orsomething, but that's behind-the-scenes optimization.~TJI propose that all the calls to any async function will be implicit(without the need of the await keyword). And precede it with a nowait onlywhen you want a promise.
See this snipped proposal:gist.github.com/jbaylina/692d4e43329c8c0d22dd
This way, the code will not have the async clutter in the logic of thefunction, but will be need to be declared async advising the programmerthat the code will not be executed atomically.
This proposal does not change the logics of the async/await proposal. Itonly change the way to call async functions. What is now implicit make themexplicit with nowait and what is explicit with await make it implicit.
I propose that all the calls to any async function will be implicit(without the need of the await keyword). And precede it with a nowait onlywhen you want a promise.See this snipped proposal:https://gist.github.com/jbaylina/692d4e43329c8c0d22ddThis way, the code will not have the async clutter in the logic of thefunction, but will be need to be declared async advising the programmerthat the code will not be executed atomically.This proposal does not change the logics of the async/await proposal. Itonly change the way to call async functions. What is now implicit make themexplicit with nowait and what is explicit with await make it implicit.-------------- next part --------------An HTML attachment was scrubbed...URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20150328/ea19062e/attachment.html>
jordi baylina schrieb:
I propose that all the calls to any async function will be implicit(without the need of the await keyword). And precede it with a nowait onlywhen you want a promise.
This way, the code will not have the async clutter in the logic of thefunction, but will be need to be declared async advising the programmerthat the code will not be executed atomically.
The problem with this is that the programmer cannot control those atoms.He only sees that the function isasync, but not where the async flowstarts, pauses, or does anything else.I don't understand whether your proposal only suggests to automaticallyawait promises on function calls, or whether that should be done inevery (sic!) single expression evaluation. The latter would becompletely ridicolous, the former still be a hazard.
You really want to be explicit about yielding control. Everything elsewill call out for error-prone code, possibly breaking invariants thateveryone expects to hold from looking at the code, just by random raceconditions. You've never hunted a heisenbug, did you?And with your proposal, you could not trust any single function call -they might completely change their behaviour (becoming async) withoutyou noticing.
Theawait keyword is definitely no "clutter".
Also have a look at this StackOverflow question&answerstackoverflow.com/a/25447289/1048572 where someone proposed a"use noblock" directive that would be very similar to your asyncfunctions. It has a nice example of such an unexpected race condition.
Kind ,Bergi
jordi baylina schrieb:> I propose that all the calls to any async function will be implicit> (without the need of the await keyword). And precede it with a nowait only> when you want a promise.>> This way, the code will not have the async clutter in the logic of the> function, but will be need to be declared async advising the programmer> that the code will not be executed atomically.The problem with this is that the programmer cannot control those atoms.He only sees that the function is `async`, but not where the async flow starts, pauses, or does anything else.I don't understand whether your proposal only suggests to automatically await promises on function calls, or whether that should be done in every (sic!) single expression evaluation. The latter would be completely ridicolous, the former still be a hazard.You really want to be explicit about yielding control. Everything else will call out for error-prone code, possibly breaking invariants that everyone expects to hold from looking at the code, just by random race conditions. You've never hunted a heisenbug, did you?And with your proposal, you could not trust any single function call - they might completely change their behaviour (becoming async) without you noticing.The `await` keyword is definitely no "clutter".Also have a look at this StackOverflow question&answer <http://stackoverflow.com/a/25447289/1048572> where someone proposed a "use noblock" directive that would be very similar to your async functions. It has a nice example of such an unexpected race condition.Kind regards, Bergi