ES6,ES7,ES8 and beyond. A Proposed Roadmap.

# Mark S. Miller (11 years ago)

(Thanks to Rick Waldron and Brendan Eich for encouragement towards posting this)

As we see on es-discuss, the pressure towards standardizing soon various features currently absent from ES6 is increasing. Rather than stretch ES6, I think we should work towards completing ES6 asap, and then proceed to ship an ES7 that is both more modest and out earlier than current expectations. I think the focus of ES7 should be parallelism, concurrency, asynchrony, and preparation for distribution. Call this the "concurrency theme". This involves at least

  • Communicating Event Loop concurrency model,
    • with the two-priority event loop already required by both browser and server.
  • Object.observe
  • Distribution compatible promises (at least Promises/A+)
  • Module Loaders
  • Weak References

I hesitate to put Module Loaders on the above list rather than leaving them in ES6; but it would allow their semantics to rest on event loops and their API to rest on promises. If we can get ES7 out quickly, this need not be as painful as it might seem, and would help us ship ES6 faster.

Elements of the concurrency theme that may be in ES7 or may be postponed to ES8:

  • RiverTrail
  • The Vat model
  • The semantics of inter-vat communications
    • including a principled alternative of structured clone
    • The emphasis being remote object messages, with postMessage and such being only one of many transports.
  • The promise hooks needed to extend promises securely over the network
    • See makeFar and makeRemote at

code.google.com/p/es-lab/source/browse/trunk/src/ses/makeQ.js#410

  • Event streams

How much of the latter we bite off in ES7 and how much we delay till ES8 will determine how quickly we can ship ES7. But however they split between ES7 and ES8, I think these should be the next things on our plate after ES6.

Some things that I think should clearly wait till ES8:

  • SES
  • Distributed persistence
  • The actual distributed cryptographic protocol for doing distributed secure persistent object communications.

Some of these should perhaps be on separate tracks within TC39, much as i18n is already on a separate track.

Some non-concurrency issues that will nevertheless be pressing in the ES7 timeframe:

  • Completing the class design
    • at least high integrity and private state
  • Quasi-parsers for JS, HTML, CSS
  • Pinning down the full semantics of inter-realm interaction within a Vat, e.g.,
    • multiple same-domain iframes
    • loader-created fresh frames
  • Possibly lexically nested modules

Although I'm posting this on three lists, discussion should proceed only on es-discuss.

Thoughts?

# Domenic Denicola (11 years ago)

This looks lovely.

The only thing I'd want to add: we need integers! And generally better numeric types. From speaking to developers on the ground, this is the biggest missing language feature they see (that isn't already addressed in ES6). I know Brendan has made some moves in this direction in SpiderMonkey, so let's just be sure it doesn't fall off the roadmap :).

Doing a quick poll of some IRC rooms, there's some call for shared-memory multithreading. I know this was a concern of the asm.js project, or more generally for the JS-as-a-compilation-target mission. I don't think this is a good idea, but just passing it along.

Finally, I know a lot of people, myself included, are excited about await sugar. That is, the plan would be to use generators + promises in ES6 with the awkwardness that entails; once we know what the prevailing patterns are we can eliminate that awkwardness with await in ES7. (I've made a sketch illustrating the idea, but of course the point of waiting is to find something that works, not the first thing I think up.) How this fits in with the concurrency strawman's more ambitious ! operator is unclear though.

# Andrea Giammarchi (11 years ago)

not sure I understand those examples, but the moment a developer starts yelding everything, is the moment all non-blovking asynchronous advantages are gone 'cause you are waiting instead of keep doing the rest, isn't it?

# Sam Tobin-Hochstadt (11 years ago)

On Sun, Apr 21, 2013 at 12:21 AM, Mark S. Miller <erights at google.com> wrote:

I hesitate to put Module Loaders on the above list rather than leaving them in ES6; but it would allow their semantics to rest on event loops and their API to rest on promises. If we can get ES7 out quickly, this need not be as painful as it might seem, and would help us ship ES6 faster.

Perhaps unsurprisingly, I think this is not a reasonable choice in the case of Module Loaders. Modules (and this would go for any possible design of modules) just aren't able to serve important the use cases in the absence of dynamic loading and configuration of where to fetch modules.

It's also the case that this doesn't avoid all of the work, it just pushes some of it onto embedders of JS, in particular HTML. I don't think that's the right choice either.

# Sam Tobin-Hochstadt (11 years ago)

On Sun, Apr 21, 2013 at 12:56 AM, Domenic Denicola <domenic at domenicdenicola.com> wrote:

Finally, I know a lot of people, myself included, are excited about await sugar. That is, the plan would be to use generators + promises in ES6 with the awkwardness that entails; once we know what the prevailing patterns are we can eliminate that awkwardness with await in ES7. (I've made [a sketch][1] illustrating the idea, but of course the point of waiting is to find something that works, not the first thing I think up.) How this fits in with the concurrency strawman's more ambitious ! operator is unclear though.

I don't see what the point of await is in your gist. It looks like all of the work is being done by function^, which looks to be sugar for creating a function and passing it to a scheduler like Q.async or taskjs.spawn. We could add that sugar if we wanted, and not need to add await.

Sam

# Bjoern Hoehrmann (11 years ago)

Andrea Giammarchi wrote:

not sure I understand those examples, but the moment a developer starts yelding everything, is the moment all non-blovking asynchronous advantages are gone 'cause you are waiting instead of keep doing the rest, isn't it?

A simple example would be an application running in a web browser. You may want to draw three images on a canvas, but the images have to be downloaded first, and while they are downloading, you still want to re- spond to user input. With only a single thread of execution, you have to yield control to the browser while awaiting the images to do so. In other words, you may not be able to keep doing the rest, but rather want to do all the other things meanwhile.

# Bjoern Hoehrmann (11 years ago)

Sam Tobin-Hochstadt wrote:

I don't see what the point of await is in your gist. It looks like all of the work is being done by function^, which looks to be sugar for creating a function and passing it to a scheduler like Q.async or taskjs.spawn. We could add that sugar if we wanted, and not need to add await.

The only language construct that allows yielding control until resumed later so far is yield, but using yield to wait is rather awkward.

# Sam Tobin-Hochstadt (11 years ago)

On Sun, Apr 21, 2013 at 9:20 AM, Bjoern Hoehrmann <derhoermi at gmx.net> wrote:

  • Sam Tobin-Hochstadt wrote:

I don't see what the point of await is in your gist. It looks like all of the work is being done by function^, which looks to be sugar for creating a function and passing it to a scheduler like Q.async or taskjs.spawn. We could add that sugar if we wanted, and not need to add await.

The only language construct that allows yielding control until resumed later so far is yield, but using yield to wait is rather awkward.

First, using yield is precisely correct in the cooperative concurrency module of JavaScript. Second, are you genuinely suggesting that we should add a new keyword with the same semantics just because the word choice might be awkward in English?

# Bjoern Hoehrmann (11 years ago)
  • Sam Tobin-Hochstadt wrote:

On Sun, Apr 21, 2013 at 9:20 AM, Bjoern Hoehrmann <derhoermi at gmx.net> wrote:

  • Sam Tobin-Hochstadt wrote:

I don't see what the point of await is in your gist. It looks like all of the work is being done by function^, which looks to be sugar for creating a function and passing it to a scheduler like Q.async or taskjs.spawn. We could add that sugar if we wanted, and not need to add await.

The only language construct that allows yielding control until resumed later so far is yield, but using yield to wait is rather awkward.

First, using yield is precisely correct in the cooperative concurrency module of JavaScript. Second, are you genuinely suggesting that we should add a new keyword with the same semantics just because the word choice might be awkward in English?

A function yielding a value to the caller with the option for the caller to resume the function later is very different from a function asking to be resumed once something has happened or has become available. I would certainly want to express the distinction in the clearest way possible, and there is nothing unusual about that, there are many other languages that allow to express this distinction. I would not define the two names so that you can always replace one by the other, though.

# David Herman (11 years ago)

On Apr 20, 2013, at 9:21 PM, Mark S. Miller <erights at google.com> wrote:

  • Communicating Event Loop concurrency model,
    • with the two-priority event loop already required by both browser and server.
  • Object.observe
  • Distribution compatible promises (at least Promises/A+)
  • Module Loaders
  • Weak References

Thanks, Mark, for getting a good discussion going here.

I agree with Sam that we need loaders and at least a minimal event loop model in ES6. As we discussed in the last meeting, we have our work to do but I believe we're on track.

I think the more important part of this conversation is the priority list, rather than nailing down exactly what has to land in what version of the spec. While the spec process has enough constant-factor overhead that spec releases themselves must be monolithic, I believe we've done a good job over the last few years moving to a more modular model (Einstein might call it "as modular as possible but no modularer," if he were as bad at English as I am). I think the ES6 process has worked well so far, setting time-based goal-posts for the spec but deciding on a feature-by-feature basis whether they make the cutoff. Of course, features interact, so they have to be designed with others in mind and sometimes they may have to land or be deferred in groups.

I agree that promises, Object.observe, and weak references are next up on the scale of being high enough priority and well-baked enough. I also agree with Domenic that value types deserve to be on this list, including integers as well as float32.

Elements of the concurrency theme that may be in ES7 or may be postponed to ES8:

  • RiverTrail
  • The Vat model
  • The semantics of inter-vat communications
    • including a principled alternative of structured clone
    • The emphasis being remote object messages, with postMessage and such being only one of many transports.
  • The promise hooks needed to extend promises securely over the network
  • Event streams

Agreed, although have some reservations about vats (and I'm just generally confused about how they relate to / differ from / interact with workers -- but that's for another thread; no pun intended). We may in fact want to consider pulling workers and structured clone into ES7/ES8, in the tradition of embracing pure computational features of JS that are currently only standardized in the DOM. It would also give us an opportunity to see if we could refine structured clone.

Some things that I think should clearly wait till ES8:

  • SES
  • Distributed persistence
  • The actual distributed cryptographic protocol for doing distributed secure persistent object communications.

Agreed.

Some of these should perhaps be on separate tracks within TC39, much as i18n is already on a separate track.

Makes sense to me!

# Brendan Eich (11 years ago)

Domenic Denicola wrote:

This looks lovely.

The only thing I'd want to add: we need integers! And generally better numeric types. From speaking to developers on the ground, this is the biggest missing language feature they see (that isn't already addressed in ES6). I know Brendan has made some moves in this direction in SpiderMonkey, so let's just be sure it doesn't fall off the roadmap :).

Thanks, I bet Mark just forgot value objects are already on the ES7 agenda.

Doing a quick poll of some IRC rooms, there's some call for shared-memory multithreading. I know this was a concern of the asm.js project, or more generally for the JS-as-a-compilation-target mission. I don't think this is a good idea, but just passing it along.

We're researching still and will not let arbitrary data races violate JS semantics (for one thing, this would hit all JITs that speculate assuming invariants obtained from JS's event-loop concurrency within the same realm, er, window -- it would break them all, AFAICT).

It may be that a Parallel JS "patient parent" model where non-overlapping slices of an array-buffer are handed off to workers, with results written back race-free, is enough for the interesting use-cases.

Wild shared-memory threading with fine-grained coordination? That is an anti-pattern in C/C++ due to the high cost of locking or equivalent lock-free techniques at scale. In related news, threads still suck.

Finally, I know a lot of people, myself included, are excited about await sugar. That is, the plan would be to use generators + promises in ES6 with the awkwardness that entails; once we know what the prevailing patterns are we can eliminate that awkwardness with await in ES7. (I've made [a sketch][1] illustrating the idea, but of course the point of waiting is to find something that works, not the first thing I think up.) How this fits in with the concurrency strawman's more ambitious ! operator is unclear though.

Not everyone is on board for making ! an infix operator via a restricted production.

As you say, we still want a clear cowpath to pave for 'await'. This means transpiler authors can help.

# David Bruant (11 years ago)

Le 21/04/2013 19:09, Brendan Eich a écrit :

Domenic Denicola wrote:

Doing a quick poll of some IRC rooms, there's some call for shared-memory multithreading. I know this was a concern of the asm.js project, or more generally for the JS-as-a-compilation-target mission. I don't think this is a good idea, but just passing it along.

We're researching still and will not let arbitrary data races violate JS semantics (for one thing, this would hit all JITs that speculate assuming invariants obtained from JS's event-loop concurrency within the same realm, er, window -- it would break them all, AFAICT).

It may be that a Parallel JS "patient parent" model where non-overlapping slices of an array-buffer are handed off to workers, with results written back race-free, is enough for the interesting use-cases.

Data ownership (like HTML5 Transferables or Rust unique pointers) is another interesting track that covers different (also interesting) use cases in my opinion. Probably to be considered as part of the ES7/ES8 work on "The semantics of inter-vat communications"

# Kevin Gadd (11 years ago)

For some subset of use cases I imagine shared-memory multithreading being limited to immutable objects could do the trick. Of course, I don't know if real immutability is actually possible in ES as currently specified - if I called Object.freeze or Object.seal on a JS object would it actually be safe to pass it to another thread and let both threads use it without any locking or guards? I imagine there are probably subtle gotchas here where 'read' operations could end up mutating state and causing threads to trip over each other. Presumably this has already been considered for transferring objects to web workers as well, i.e. if the object is known immutable, it can be 'shared' with the worker without a copy.

Combining a graph of immutable objects (like a source AST or scene graph or something like that) with a transferable arraybuffer would let you tackle lots of use cases, I think, without as many downsides as true shared-memory threading. But I don't know if that addresses enough use cases to merit the effort.

# Sam Tobin-Hochstadt (11 years ago)

On Sun, Apr 21, 2013 at 6:33 PM, Kevin Gadd <kevin.gadd at gmail.com> wrote:

if I called Object.freeze or Object.seal on a JS object would it actually be safe to pass it to another thread and let both threads use it without any locking or guards?

No, it's not safe. Consider Object.freeze(console.log).

# Kevin Gadd (11 years ago)

isn't typeof (console.log) "function" and not "object"? by object I meant an Object object, not 'any arbitrary JS value'. I suppose the distinction is really blurry, and you would still have to deal with problems like the object's prototype chain containing callables...

# Sam Tobin-Hochstadt (11 years ago)

Then consider Object.freeze(console).

Fundamentally, frozen objects are not appropriate for prohibiting shared state.

# Mark S. Miller (11 years ago)

Agreed that Object.freeze does not give you what you need.

RiverTrail already needs a kind of collection that is transitively immutable by construction. They need this for safe data parallelism. But the reason they are safe for parallel access within a vat/worker is the same reason they would be safe to pass-by-sharing between vats/workers that share an address space. And between address spaces, including between machines, they would be passed by copy, but without any observable difference beyond performance. We should keep this in mind as we proceed on the RiverTrail work.

# Dean Landolt (11 years ago)

On Sun, Apr 21, 2013 at 9:29 PM, Mark S. Miller <erights at google.com> wrote:

Agreed that Object.freeze does not give you what you need.

RiverTrail already needs a kind of collection that is transitively immutable by construction. They need this for safe data parallelism. But the reason they are safe for parallel access within a vat/worker is the same reason they would be safe to pass-by-sharing between vats/workers that share an address space. And between address spaces, including between machines, they would be passed by copy, but without any observable difference beyond performance. We should keep this in mind as we proceed on the RiverTrail work.

I completely agree with Mark, and would add that there's a lot to be learned from the kind of transitive immutability employed by clojure's persistent data structures. Bringing this kind of thing to js could be pretty straitforward, and IIUC modern js vms already employ this kind of structural sharing in the form of ropes for string manipulation. If this kind of primitive could be extended to userland (perhaps in conjunction with value types) it would be a huge win for concurrency (safe and free to pass between workers -- no ownership transfer semantics necessary), parallelism (RiverTrail would eat this up), but it could be an even bigger win for run-of-the-mill js code.

Persistent immutable data structures would be a big usability improvement in their own right. In a world where it's just as cheap and convenient to use immutable structures as it is to depend on side-effects, code that does this (including your own) would be a whole lot easier to reason about and would have objectively better security properties. I'd think this alone would justify their consideration -- but as Mark suggested, they could also have real performance advantages given RiverTrail or workers.

But I'll double down on that and suggest there are perf wins to be had for regulation js code w/o any concurrency or parallelism primitives. For instance, a persistent immutable array primitive backed by something like a fingertree would have some really interesting characteristics compared to a standard js array -- efficient splicing, binary search, deque operations, constant-time reverse, to name a few. And I won't even start on the amazingly cool things that could be done if you could provide group calculations for caching in the branches of your tree (except to say that js devs should be shouting about monoids, not monads!). And of course this kind of group-theoretical approach could extend naturally to RiverTrail code as well...

I bring this up because I think these kinds of optimizations and performance-specific features would be interesting and useful to explore, but having said all that, I believe the utility of persistent data structures stands on it's own, regardless of possible perf wins (parallel, concurrent or otherwise). I would love to see this discussed along with value types for es7.

# Russell Leggett (11 years ago)

Persistent immutable data structures would be a big usability improvement in their own right. In a world where it's just as cheap and convenient to use immutable structures as it is to depend on side-effects, code that does this (including your own) would be a whole lot easier to reason about and would have objectively better security properties. I'd think this alone would justify their consideration -- but as Mark suggested, they could also have real performance advantages given RiverTrail or workers.

But I'll double down on that and suggest there are perf wins to be had for regulation js code w/o any concurrency or parallelism primitives. For instance, a persistent immutable array primitive backed by something like a fingertree would have some really interesting characteristics compared to a standard js array -- efficient splicing, binary search, deque operations, constant-time reverse, to name a few. And I won't even start on the amazingly cool things that could be done if you could provide group calculations for caching in the branches of your tree (except to say that js devs should be shouting about monoids, not monads!). And of course this kind of group-theoretical approach could extend naturally to RiverTrail code as well...

I bring this up because I think these kinds of optimizations and performance-specific features would be interesting and useful to explore, but having said all that, I believe the utility of persistent data structures stands on it's own, regardless of possible perf wins (parallel, concurrent or otherwise). I would love to see this discussed along with value types for es7.

+1000 I would love to see persistent immutable data structures part of ES7. I think they would be a fantastic fit for both concurrent and non-concurrent js code.

# Domenic Denicola (11 years ago)

From: Sam Tobin-Hochstadt [samth at ccs.neu.edu]

I don't see what the point of await is in your gist. It looks like all of the work is being done by function^, which looks to be sugar for creating a function and passing it to a scheduler like Q.async or taskjs.spawn. We could add that sugar if we wanted, and not need to add await.

It's all a bit fuzzy in my head, I admit, but the idea is that yield should not have its meaning "taken over" by the promises-scheduler meaning. E.g. you could envision something that used both promises and generators, perhaps to yield a list of values retrieved asynchronously:

// ES6

function* doubleSomeNumbers() {
  return getNumbersToDouble().then(function* (numbers) {
    for (var i = 0; i < numbers.length; ++i) {
      yield numbers[i] * 2;
    }
  });
}


// ES-after-6:

function*^ doubleSomeNumbers() {
  const numbers = await getNumbersToDouble();

  for (var i = 0; i < numbers.length; ++i) {
    yield numbers[i] * 2;
  }
}

This is obviously contrived, but I think illustrates how you might want to use both yield and await for their different meanings, instead of letting the wait-for-a-promise meaning take over yield.

# Sam Tobin-Hochstadt (11 years ago)

What exactly would be the semantic difference between this and just using yield?

# Domenic Denicola (11 years ago)

From: samth0 at gmail.com [samth0 at gmail.com]

What exactly would be the semantic difference between this and just using 'yield'?

You mean, if you replaced my example by

function*^ doubleSomeNumbers() {
  const numbers = yield getNumbersToDouble();

  for (var i = 0; i < numbers.length; ++i) {
    yield numbers[i] * 2;
  }
}

?

My thinking was that function* returns a generator, function^ returns a promise, and function*^ returns a promise for a generator. Thus:

  • The original version from my previous message returns a promise for a generator that yields a sequence of doubled numbers, which settles only after waiting for the promise returned from getNumbersToDouble to settle (and will reject if that promise rejects`).
  • Whereas, this new version returns a promise that is always fulfilled, immediately (since there's no await or explicit promise-working). Its fulfillment value is a generator which first yields the promise returned by getNumbersToDouble, then yields the doubled numbers.
# David Herman (11 years ago)

On Apr 21, 2013, at 2:05 PM, David Bruant <bruant.d at gmail.com> wrote:

Data ownership (like HTML5 Transferables or Rust unique pointers) is another interesting track that covers different (also interesting) use cases in my opinion. Probably to be considered as part of the ES7/ES8 work on "The semantics of inter-vat communications"

I've been working on this lately. More later.

# Tab Atkins Jr. (11 years ago)

On Mon, Apr 22, 2013 at 2:45 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:

What exactly would be the semantic difference between this and just using 'yield'?

The semantic difference is that 'yield' pauses your execution and gives control to the calling code, while 'await' pauses your execution and gives control to the promise. Completely different direction of control-passing.

It so happens that you can use an async manager wrapped around your function to make the first look like the second, but that doesn't mean they're the same thing, and it doesn't mean that we want to adopt that kind of semantic for the language itself.

# Bjoern Hoehrmann (11 years ago)
  • Sam Tobin-Hochstadt wrote:

What exactly would be the semantic difference between this and just using 'yield'?

If you consider it from the perspective of someone reading the code, you might find, as an example, try { ... yield ... } rather weird (how can yielding control fail?) while try { ... await ... } is fairly obvious.

# Andy Wingo (11 years ago)

On Tue 23 Apr 2013 01:31, "Tab Atkins Jr." <jackalmage at gmail.com> writes:

On Mon, Apr 22, 2013 at 2:45 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:

What exactly would be the semantic difference between this and just using 'yield'?

The semantic difference is that 'yield' pauses your execution and gives control to the calling code, while 'await' pauses your execution and gives control to the promise. Completely different direction of control-passing.

It seems quite unlikely that modern JS implementations would implement `await' as you describe it. It's relatively easy to suspend computation in one function frame, which is why we can have nice things like yield. Suspending multiple function activations is done in some language environments, but it would be difficult to retrofit into JS implementations, which is why we can't have nice things like await.

My 2c.

Andy

# David Bruant (11 years ago)

Le 23/04/2013 01:31, Tab Atkins Jr. a ?crit :

On Mon, Apr 22, 2013 at 2:45 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:

What exactly would be the semantic difference between this and just using 'yield'?

The semantic difference is that 'yield' pauses your execution and gives control to the calling code, while 'await' pauses your execution and gives control to the promise. Completely different direction of control-passing.

Your description reminds me of coroutines and Dave Herman's article about it 1. Is await immune from the issues described as: "Once you add coroutines, you never know when someone might call yield [considered as a stack pause primitive]. Any function you call has the right to pause and resume you whenever they want, even after any number of spins of the event loop. Now any time you find yourself modifying state, you start worrying that calling a function might interrupt some code you intended to be transactional. "

Overall, control-flow related syntax cannot give you authority that goes beyond your own frame. If that happens, then any library (think Node.js modules which are recursively by hundreds in any decent-sized project, so you don't have to to review them all) can start pretend being smart and mess with you invariants if you expected the library function to return (and that's a very natural thing to expect).

# Kevin Gadd (11 years ago)

My information may be incomplete (I haven't used await since the beta of the release that introduced it), but 'await' is part of the function's return type/signature in C#; that is, a function that may await has a different signature than a function that may not.

Calling a function that may await during its execution returns a value that must be 'awaited' so things propagate out nicely and there are no surprises.

Furthermore, at least when discussing the C# version of the 'await' concept, it does NOT suspend execution of anything. It is NOT a coroutine. It is a callback-passing transform, wherein a function that uses await is mechanically transformed by the compiler into a series of callbacks and the compiler mechanically ensures that the callbacks are passed into the .NET equivalent of promises (Task<T>) in order to resume

execution at the appropriate time.

If the intent is to model a proposed ES 'await' on another language, my apologies: the only major implementation using this keyword that I know of is C#. If the intent is to be inspired in any degree by C#'s it's worth reading up on it and checking out some of the examples; I'd especially encourage you to look at the output of the compiler transform. It's less complex than one might think and the biggest complication (in my opinion) is how it influences lifetime management. msdn.microsoft.com/en-us/library/vstudio/hh156528.aspx

One way to look at it is that await and yield are sort of inverses: yield mechanically transforms a function into a state machine that is driven from the outside by someone advancing an enumerator, while await mechanically transforms a function into a state machine that is driven 'internally' by the function chaining the continuation of its execution to the result of some future that it implicitly owns (or has been passed ownership of).

In the C# model, 'await' can be used on any value that is 'awaitable', which is (based on my last discussion with the designer of the keyword) defined simply as the object having a 'GetAwaiter' method, which returns an object you can use to chain a callback to the fulfillment of the value. To me this is essentially a narrowly defined interface that represents the 'then'/'OnComplete'/'registerCallback' portion of the typical Future/Promise consumer interface.

Part of the importance of await as a mechanism is that it removes the need for an external driver, like the task scheduler implied by systems like task.js and my own task scheduler. It also integrates much more simply into callback-oriented async models than an approach based on yield and task schedulers does. The clarity of the definition of 'await' is also valuable in this regard, as there is no difference between an enumerator being used as a coroutine and an enumerator being used as an enumerator as far as their types go - you can't just look at the definition or body of the function and immediately know, 'ah, this is a coroutine'. You have to infer that based on how it is used and what kind of values it yields.

If you have more precise questions about the concept and how it works, I can try and dig up the archived conversation I had with the keyword's designers and see if there are any relevant quotes to share here, or perhaps even ask them to try and answer any questions you have that I can't.

As a final note, it is definitely the case that 'await' was fully expressible mechanically in previous versions of C#. It follows the C# tradition of replacing common idioms and patterns with compiler-generated versions of those patterns that are easier to write and more robust against mistakes; things like error propagation and lifetime management in particular are greatly simplified by the compiler's aid. I think this is consistent with the approach TC39 is taking with things like the module system so considering a similar feature is at least a good match. I don't have a strong opinion as to whether JavaScript needs 'await', beyond that the traditional callback-passing style of JS async programming is a complete nightmare.

# Mark S. Miller (11 years ago)

On Tue, Apr 23, 2013 at 12:12 AM, Andy Wingo <wingo at igalia.com> wrote:

On Tue 23 Apr 2013 01:31, "Tab Atkins Jr." <jackalmage at gmail.com> writes:

On Mon, Apr 22, 2013 at 2:45 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:

What exactly would be the semantic difference between this and just using

'yield'?

The semantic difference is that 'yield' pauses your execution and gives control to the calling code, while 'await' pauses your execution and gives control to the promise. Completely different direction of control-passing.

It seems quite unlikely that modern JS implementations would implement `await' as you describe it. It's relatively easy to suspend computation in one function frame, which is why we can have nice things like yield. Suspending multiple function activations is done in some language environments, but it would be difficult to retrofit into JS implementations, which is why we can't have nice things like await.

Hi Andy, look again at Domenic's gist at < gist.github.com/domenic/5428522> and the Q.async definition it uses

at < strawman:async_functions#reference_implementation>.

I think the only proposed difference between "await" and "yield" is the name. The notion is that, when using ES6 generators and yield to get the effect associated with "await" in C#, he'd rather use the term "await" at the same time he's rather say "function^ ...." rather than "Q.async(function* ....". Other than these two simple local desugarings, "yield" and "await" are synonyms.

The only issue at stake is whether the each bit of sugar is worth the cost. Fortunately, we'll gain experience with Domenic's middle pattern in ES6. In fact, we can start gaining experience with it today using Kris Kowal's Q on FF generators, which already work on ES5 on FF. Armed with this experience, we can then make an informed decision.

My 2c.

FWIW, my 2c agrees with Sam on "await" vs "yield". If async functions are not common, then adding sugar for a small benefit isn't worth it. If async functions are common, then "yield" will come to be understood in the more general way that accounts both for their use in generators and their use in async functions. I doubt the "await" renaming is of adequate benefit to either scenario. People coming from C# can learn to say "yield" where they were used to saying "await".

On something like "function^ ...." vs "Q.async(function* ...." I am more undecided. If async functions are common enough, I can see this additional bit of sugar possibly being worth it. It would be like the difference between "function" and arrow functions all over again. But we lived with "function" functions for a long time without too much pain. Even if async functions become common, I am skeptical this additional sugaring is urgent. Let's give our experience of these more time to cook.

# Mark S. Miller (11 years ago)

Hi David, see my response to Andy. await is just yield within a Q.async(function*. Both async-functions/await and generators/yield are shallow. Thus neither create the hazards of co-routines.

On Tue, Apr 23, 2013 at 1:26 AM, David Bruant <bruant.d at gmail.com> wrote:

Le 23/04/2013 01:31, Tab Atkins Jr. a ?crit :

On Mon, Apr 22, 2013 at 2:45 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu>

wrote:

What exactly would be the semantic difference between this and just using 'yield'?

The semantic difference is that 'yield' pauses your execution and gives control to the calling code, while 'await' pauses your execution and gives control to the promise. Completely different direction of control-passing.

Your description reminds me of coroutines and Dave Herman's article about it [1]. Is await immune from the issues described as: "Once you add coroutines, you never know when someone might call yield [considered as a stack pause primitive]. Any function you call has the right to pause and resume you whenever they want, even after any number of spins of the event loop. Now any time you find yourself modifying state, you start worrying that calling a function might interrupt some code you intended to be transactional. "

Overall, control-flow related syntax cannot give you authority that goes beyond your own frame. If that happens, then any library (think Node.js modules which are recursively by hundreds in any decent-sized project, so you don't have to to review them all) can start pretend being smart and mess with you invariants if you expected the library function to return (and that's a very natural thing to expect).

David

[1] calculist.org/blog2011/12/14/why-coroutines- wont-work-on-the-web/calculist.org/blog/2011/12/14/why-coroutines-wont-work-on-the-web

-- Cheers, --MarkM -------------- next part -------------- An HTML attachment was scrubbed... URL: esdiscuss/attachments/20130423/d4baca19/attachment-0001

# Mark S. Miller (11 years ago)

you are correct that it isn't a co-routine, and that it corresponds to a mechanical CPS rewrite of only the local function itself. Fortunately, it is exactly the same CPS rewrite required to account for the semantics of generators/yield as a mechanical transform. This is in fact how Traceur implements generators.

# Andy Wingo (11 years ago)

On Tue 23 Apr 2013 13:56, "Mark S. Miller" <erights at google.com> writes:

Hi Andy, look again at Domenic's gist at gist.github.com/domenic/5428522 and the Q.async definition it uses at <strawman:async_functions#reference_ implementation>. I think the only proposed difference between "await" and "yield" is the name. The notion is that, when using ES6 generators and yield to get the effect associated with "await" in C#, he'd rather use the term "await" at the same time he's rather say "function^ ...." rather than "Q.async(function* ....". Other than these two simple local desugarings, "yield" and "await" are synonyms.

Yes, I think I misunderstood the proposal. Thanks for clarifying it for me, and apologies for the noise!

# Tab Atkins Jr. (11 years ago)

On Tue, Apr 23, 2013 at 12:12 AM, Andy Wingo <wingo at igalia.com> wrote:

On Tue 23 Apr 2013 01:31, "Tab Atkins Jr." <jackalmage at gmail.com> writes:

On Mon, Apr 22, 2013 at 2:45 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:

What exactly would be the semantic difference between this and just using 'yield'?

The semantic difference is that 'yield' pauses your execution and gives control to the calling code, while 'await' pauses your execution and gives control to the promise. Completely different direction of control-passing.

It seems quite unlikely that modern JS implementations would implement `await' as you describe it. It's relatively easy to suspend computation in one function frame, which is why we can have nice things like yield. Suspending multiple function activations is done in some language environments, but it would be difficult to retrofit into JS implementations, which is why we can't have nice things like await.

Apologies if I was unclear and caused you to jump to an incorrect conclusion with my wording, but I was referring to what Keven Gadd describes - an async function returns a future that completes when the function does, and this can be realized through a mechanical source-text transformation. (I have no knowledge of how implementations would prefer to realize this, but I'd suspect they'd actually use something like Task/Q's async manager, using some variation on generator semantics internally.)

# Bill Frantz (11 years ago)

On 4/21/13 at 10:09 AM, brendan at mozilla.com (Brendan Eich) wrote:

Wild shared-memory threading with fine-grained coordination? That is an anti-pattern in C/C++ due to the high cost of locking or equivalent lock-free techniques at scale. In related news, threads still suck.


Bill Frantz | Concurrency is hard. 12 out | Periwinkle (408)356-8506 | 10 programmers get it wrong. | 16345 Englewood Ave www.pwpconsult.com | - Jeff Frantz | Los Gatos, CA 95032

# Tab Atkins Jr. (11 years ago)

On Tue, Apr 23, 2013 at 4:56 AM, Mark S. Miller <erights at google.com> wrote:

On Tue, Apr 23, 2013 at 12:12 AM, Andy Wingo <wingo at igalia.com> wrote:

On Tue 23 Apr 2013 01:31, "Tab Atkins Jr." <jackalmage at gmail.com> writes:

On Mon, Apr 22, 2013 at 2:45 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:

What exactly would be the semantic difference between this and just using 'yield'?

The semantic difference is that 'yield' pauses your execution and gives control to the calling code, while 'await' pauses your execution and gives control to the promise. Completely different direction of control-passing.

It seems quite unlikely that modern JS implementations would implement `await' as you describe it. It's relatively easy to suspend computation in one function frame, which is why we can have nice things like yield. Suspending multiple function activations is done in some language environments, but it would be difficult to retrofit into JS implementations, which is why we can't have nice things like await.

Hi Andy, look again at Domenic's gist at gist.github.com/domenic/5428522 and the Q.async definition it uses at strawman:async_functions#reference_implementation. I think the only proposed difference between "await" and "yield" is the name. The notion is that, when using ES6 generators and yield to get the effect associated with "await" in C#, he'd rather use the term "await" at the same time he's rather say "function^ ...." rather than "Q.async(function* ....". Other than these two simple local desugarings, "yield" and "await" are synonyms.

The only issue at stake is whether the each bit of sugar is worth the cost. Fortunately, we'll gain experience with Domenic's middle pattern in ES6. In fact, we can start gaining experience with it today using Kris Kowal's Q on FF generators, which already work on ES5 on FF. Armed with this experience, we can then make an informed decision.

My 2c.

FWIW, my 2c agrees with Sam on "await" vs "yield". If async functions are not common, then adding sugar for a small benefit isn't worth it. If async functions are common, then "yield" will come to be understood in the more general way that accounts both for their use in generators and their use in async functions. I doubt the "await" renaming is of adequate benefit to either scenario. People coming from C# can learn to say "yield" where they were used to saying "await".

On something like "function^ ...." vs "Q.async(function* ...." I am more undecided. If async functions are common enough, I can see this additional bit of sugar possibly being worth it. It would be like the difference between "function" and arrow functions all over again. But we lived with "function" functions for a long time without too much pain. Even if async functions become common, I am skeptical this additional sugaring is urgent. Let's give our experience of these more time to cook.

I don't think yield and await are similar at all. You need to interpose a control inverter between yourself and the outside world to have them work similarly. They just both represent "freeze me, and start me back up when something else happens", but that doesn't make them identical, just related.

Further, overloading 'yield' means we can't produce an async generator, which seems useful in itself. Domenic produced a silly trivial example, but it's easy to imagine the usefulness of this in non-trivial cases: for example, a generator that uses an XHR to generate each value. This would produce a generator which yielded a future each time it was called.

(This also seems to be almost precisely the semantics I was shooting for with my "single-listener, non-lossy" event stream concept, which is nice.)

# Erik Arvidsson (11 years ago)

On Tue, Apr 23, 2013 at 8:01 AM, Mark S. Miller <erights at google.com> wrote:

Hi Kevin, you are correct that it isn't a co-routine, and that it corresponds to a mechanical CPS rewrite of only the local function itself. Fortunately, it is exactly the same CPS rewrite required to account for the semantics of generators/yield as a mechanical transform. This is in fact how Traceur implements generators.

And how we implement 'await' too. goo.gl/QKXO0

For Traceur both GeneratorTransformer and AsyncTransformer extends CPSTransformer which does the heavy work of building the state machine.

We proposed await at Redmond 2011 and showed our implementations at JsConf2011

# Till Schneidereit (11 years ago)

Is there any chance at all of macros á la sweet.js will be on the agenda for ES7 (or 8)?

I would very much like for that to happen to enable even more, and more light-weight, experimentation with and cow-path building for the syntactic language surface.

# Erik Arvidsson (11 years ago)

Also, see Peter Hallams part from NodeConf 2011 where he walks through how await works: youtu.be/ntDZa7ekFEA?t=17m2s

# Anne van Kesteren (11 years ago)

On Sun, Apr 21, 2013 at 5:21 AM, Mark S. Miller <erights at google.com> wrote:

Thoughts?

I was wondering when operator overloading is planned. It would be great to be able to do things like new URL("example.org") == new URL("example.org").

-- annevankesteren.nl

# Dean Landolt (11 years ago)

On Wed, Apr 24, 2013 at 11:04 AM, Anne van Kesteren <annevk at annevk.nl>wrote:

On Sun, Apr 21, 2013 at 5:21 AM, Mark S. Miller <erights at google.com> wrote:

Thoughts?

I was wondering when operator overloading is planned. It would be great to be able to do things like new URL("example.org") == new URL("example.org").

I'm not sure if there are any plans for overloading but value objects [1] will solve the specific use case you gave.

[1] strawman:value_objects

# David Bruant (11 years ago)

Le 24/04/2013 17:20, Dean Landolt a écrit :

On Wed, Apr 24, 2013 at 11:04 AM, Anne van Kesteren <annevk at annevk.nl <mailto:annevk at annevk.nl>> wrote:

On Sun, Apr 21, 2013 at 5:21 AM, Mark S. Miller
<erights at google.com <mailto:erights at google.com>> wrote:
> Thoughts?

I was wondering when operator overloading is planned. It would be
great to be able to do things like new URL("http://example.org") ==
new URL("http://example.org/").

The same object could be returned if immutable

I'm not sure if there are any plans for overloading but value objects [1] will solve the specific use case you gave.

[1] strawman:value_objects

value proxies could be used too I think strawman:value_proxies

# Mark S. Miller (11 years ago)

The Roadmap will be on the agenda for the upcoming TC39 May meeting. Many of us want macros eventually, the ES6 module system gives us the stage separation necessary for clean macros, and I don't recall any objections to eventually including macros if they are clean enough. But macros are a big hard design space, and the most explored form of cleanliness, known as "hygiene", is insufficient by itself. I cannot imagine discussions of macros converging fast enough for ES7, given the urgency for shipping ES7 quickly. I do think it makes sense to add macros to the agenda for ES8.

# Till Schneidereit (11 years ago)

Thanks, that makes a lot of sense to me.

# Dmitry Lomov (10 years ago)

(Somehow missed this thread) Typed objects aka binary data should be on our ES7 agenda as well.

# Mark S. Miller (10 years ago)

Indeed. Thanks for reviving this thread. Altogether, I'm pleasantly surprised how well this old thread holds up in light of more recent events.