Does async/await solve a real problem?

Florian Bösch pyalot at
Thu Sep 11 07:22:51 PDT 2014

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

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> wrote:

> On Thu, Sep 11, 2014 at 6:20 AM, Florian Bösch <pyalot at> 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 <>
> 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>
>> wrote:
>>> Also, see
>>> for previous discussion.
>>> On Thu, Sep 11, 2014 at 8:42 AM, Domenic Denicola <
>>> domenic at> 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] On Behalf Of
>>>> Jeswin Kumar
>>>> Sent: Thursday, September 11, 2014 11:46
>>>> To: es-discuss at
>>>> 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
>>>> 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:
>>>> 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...
>>>> _______________________________________________
>>>> es-discuss mailing list
>>>> es-discuss at
>>>> _______________________________________________
>>>> es-discuss mailing list
>>>> es-discuss at
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at
> --
>     Cheers,
>     --MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the es-discuss mailing list