This proposal has progressed to the Draft ECMAScript 6 Specification, which is available for review here: specification_drafts. Any new issues relating to them should be filed as bugs at http://bugs.ecmascript.org. The content on this page is for historic record only and may no longer reflect the current state of the feature described within.
First-class coroutines, represented as objects encapsulating suspended execution contexts (i.e., function activations). Prior art: Python, Icon, Lua, Scheme, Smalltalk.
The “infinite” sequence of Fibonacci numbers (notwithstanding behavior around 253):
function* fibonacci() { let [prev, curr] = [0, 1]; for (;;) { [prev, curr] = [curr, prev + curr]; yield curr; } }
Generators can be iterated over in loops:
for (n of fibonacci()) { // truncate the sequence at 1000 if (n > 1000) break; print(n); }
Generators are iterators:
let seq = fibonacci(); print(seq.next()); // 1 print(seq.next()); // 2 print(seq.next()); // 3 print(seq.next()); // 5 print(seq.next()); // 8
See the “@iter” module in iterators.
Every generator object has the following internal properties:
Object.prototype
There are four function objects, send, next, throw, and close. Every generator object has four properties, send, next, throw, and close, all respectively pointing to their corresponding function value. The functions’ behavior is specified below.
The function syntax is extended to add an optional * token:
FunctionDeclaration:
"function" "*"? Identifier "(" FormalParameterList? ")" "{" FunctionBody "}"
FunctionExpression:
"function" "*"? Identifier? "(" FormalParameterList? ")" "{" FunctionBody "}"
A function with a * token is known as a generator function. The following two unary operators are only allowed in the immediate body of a generator function (i.e., in the body but not nested inside another function):
AssignmentExpression:
...
YieldExpression
YieldExpression:
"yield" ("*"? AssignmentExpression)?
An early error is raised if a yield or yield* expression occurs in a non-generator function.
This section describes the semantics of generator functions.
Let f be a generator function. The semantics of a function call f(x1, ..., xn) is:
Let E = a new VariableEnvironment record with mappings for x1 ... xn
Let S = the current scope chain extended with E
Let V = a new generator object with
[[Scope]] = S
[[Code]] = f.[[Code]]
[[ExecutionContext]] = null
[[State]] = “newborn”
[[Handler]] = the standard generator handler
Return V
The semantics of evaluating an expression of the form yield e is:
Let V ?= Evaluate(e)
Let K = the current execution context
Let O = K.currentGenerator
O.[[ExecutionContext]] := K
O.[[State]] := “suspended”
Pop the current execution context
Return (normal, V, null)
The yield* operator delegates to another generator. This provides a convenient mechanism for composing generators.
The expression yield* <<expr>> is equivalent to:
let (g = <<expr>>) { let received = void 0, send = true, result = void 0; try { while (true) { let next = send ? g.send(received) : g.throw(received); try { received = yield next; send = true; } catch (e) { received = e; send = false; } } } catch (e) { if (!isStopIteration(e)) throw e; result = e.value; } finally { try { g.close(); } catch (ignored) { } } result }
This is similar to a for-in loop over the generator, except that it propagates exceptions thrown via the outer generator’s throw method into the delegated generator.
The semantics of return e inside a generator function is:
Let V ?= Evaluate(e)
Let K = the current execution context
Let O = K.currentGenerator
O.[[State]] := “closed”
Let R = a new object with
[[Class]] = “StopIteration”
R.value := V
Throw R
See iterators for a discussion of StopIteration.
As in ordinary functions, return; is equivalent to return (void 0);, and if control falls off the end of a generator function body, the generator function performs an implicit return;.
The next function’s behavior is:
If this is not a generator object, Throw Error
Call this.[[Send]] with single argument undefined
Return the result
The send function’s behavior is:
If this is not a generator object, Throw Error
Call this.[[Send]] with the first argument
Return the result
The throw function’s behavior is:
If this is not a generator object, Throw Error
Call this.[[Throw]] with the first argument
Return the result
The close function’s behavior is:
If this is not a generator object, Throw Error
Call this.[[Close]] with no arguments
Return the result
Every generator is an iterator object, and it has an iterate method whose behavior is:
Return this
In other words, generator iterators can be automatically used with for-of loops and comprehensions.
A generator object can be in one of four states:
It is never the case that G.[[Code]] != null ∧ G.[[ExecutionContext]] != null.
G.[[Send]]
Let State = G.[[State]]
If State = “executing” Throw Error
If State = “closed” Throw Error
Let X be the first argument
If State = “newborn”
If X != undefined Throw TypeError
Let K = a new execution context as for a function call
K.currentGenerator := G
K.scopeChain := G.[[Scope]]
Push K onto the stack
Return Execute(G.[[Code]])
G.[[State]] := “executing”
Let Result = Resume(G.[[ExecutionContext]], normal, X)
Return Result
G.[[Throw]]
Let State = G.[[State]]
If State = “executing” Throw Error
If State = “closed” Throw Error
Let X be the first argument
If State = “newborn”
G.[[State]] := “closed”
G.[[Code]] := null
Return (throw, X, null)
G.[[State]] := “executing”
Let Result = Resume(G.[[ExecutionContext]], throw, X)
Return Result
The close method terminates a suspended generator. This informs the generator to resume roughly as if via return, running any active finally blocks first before completing.
G.[[Close]]
Let State = G.[[State]]
If State = “executing” Throw Error
If State = “closed” Return undefined
If State = “newborn”
G.[[State]] := “closed”
G.[[Code]] := null
Return (normal, undefined, null)
G.[[State]] := “executing”
Let Result = Resume(G.[[ExecutionContext]], return, undefined)
G.[[State]] := “closed”
Return Result
(This operation assumes that we re-specify expressions to have completion types just like statements.)
Operation Resume(K, completionType, V)
Push K onto the execution context stack
Let G = K.currentGenerator
Set the current scope chain to G.[[Scope]]
Continue executing K as if its last expression produced (completionType, V, null)