From f4694412639e19acab2c0dd5baac247aa7d7a889 Mon Sep 17 00:00:00 2001 From: Kevin Gibbons Date: Wed, 7 Jul 2021 17:45:18 -0600 Subject: [PATCH] Editorial: Clarify the AsyncGenerator state machine (#2413) --- spec.html | 252 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 155 insertions(+), 97 deletions(-) diff --git a/spec.html b/spec.html index fc8b7925f4..626dc5c4da 100644 --- a/spec.html +++ b/spec.html @@ -39858,8 +39858,21 @@

AsyncGenerator.prototype.constructor

AsyncGenerator.prototype.next ( _value_ )

1. Let _generator_ be the *this* value. + 1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%). + 1. Let _result_ be AsyncGeneratorValidate(_generator_, ~empty~). + 1. IfAbruptRejectPromise(_result_, _promiseCapability_). + 1. Let _state_ be _generator_.[[AsyncGeneratorState]]. + 1. If _state_ is ~completed~, then + 1. Let _iteratorResult_ be ! CreateIterResultObject(*undefined*, *true*). + 1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _iteratorResult_ »). + 1. Return _promiseCapability_.[[Promise]]. 1. Let _completion_ be NormalCompletion(_value_). - 1. Return ! AsyncGeneratorEnqueue(_generator_, _completion_, ~empty~). + 1. Perform ! AsyncGeneratorEnqueue(_generator_, _completion_, _promiseCapability_). + 1. If _state_ is either ~suspendedStart~ or ~suspendedYield~, then + 1. Perform ! AsyncGeneratorResume(_generator_, _completion_). + 1. Else, + 1. Assert: _state_ is either ~executing~ or ~awaiting-return~. + 1. Return _promiseCapability_.[[Promise]]. @@ -39867,8 +39880,20 @@

AsyncGenerator.prototype.next ( _value_ )

AsyncGenerator.prototype.return ( _value_ )

1. Let _generator_ be the *this* value. + 1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%). + 1. Let _result_ be AsyncGeneratorValidate(_generator_, ~empty~). + 1. IfAbruptRejectPromise(_result_, _promiseCapability_). 1. Let _completion_ be Completion { [[Type]]: ~return~, [[Value]]: _value_, [[Target]]: ~empty~ }. - 1. Return ! AsyncGeneratorEnqueue(_generator_, _completion_, ~empty~). + 1. Perform ! AsyncGeneratorEnqueue(_generator_, _completion_, _promiseCapability_). + 1. Let _state_ be _generator_.[[AsyncGeneratorState]]. + 1. If _state_ is either ~suspendedStart~ or ~completed~, then + 1. Set _generator_.[[AsyncGeneratorState]] to ~awaiting-return~. + 1. Perform ! AsyncGeneratorAwaitReturn(_generator_). + 1. Else if _state_ is ~suspendedYield~, then + 1. Perform ! AsyncGeneratorResume(_generator_, _completion_). + 1. Else, + 1. Assert: _state_ is either ~executing~ or ~awaiting-return~. + 1. Return _promiseCapability_.[[Promise]]. @@ -39876,8 +39901,23 @@

AsyncGenerator.prototype.return ( _value_ )

AsyncGenerator.prototype.throw ( _exception_ )

1. Let _generator_ be the *this* value. + 1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%). + 1. Let _result_ be AsyncGeneratorValidate(_generator_, ~empty~). + 1. IfAbruptRejectPromise(_result_, _promiseCapability_). + 1. Let _state_ be _generator_.[[AsyncGeneratorState]]. + 1. If _state_ is ~suspendedStart~, then + 1. Set _generator_.[[AsyncGeneratorState]] to ~completed~. + 1. Set _state_ to ~completed~. + 1. If _state_ is ~completed~, then + 1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « _exception_ »). + 1. Return _promiseCapability_.[[Promise]]. 1. Let _completion_ be ThrowCompletion(_exception_). - 1. Return ! AsyncGeneratorEnqueue(_generator_, _completion_, ~empty~). + 1. Perform ! AsyncGeneratorEnqueue(_generator_, _completion_, _promiseCapability_). + 1. If _state_ is ~suspendedYield~, then + 1. Perform ! AsyncGeneratorResume(_generator_, _completion_). + 1. Else, + 1. Assert: _state_ is either ~executing~ or ~awaiting-return~. + 1. Return _promiseCapability_.[[Promise]]. @@ -39908,7 +39948,7 @@

Properties of AsyncGenerator Instances

[[AsyncGeneratorQueue]] - A List of AsyncGeneratorRequest records which represent requests to resume the async generator. + A List of AsyncGeneratorRequest records which represent requests to resume the async generator. Except during state transitions, it is nonempty if and only if [[AsyncGeneratorState]] is either ~executing~ or ~awaiting-return~. [[GeneratorBrand]] @@ -39923,7 +39963,7 @@

Properties of AsyncGenerator Instances

AsyncGenerator Abstract Operations

AsyncGeneratorRequest Records

-

The AsyncGeneratorRequest is a Record value used to store information about how an async generator should be resumed and contains capabilities for fulfilling or rejecting the corresponding promise.

+

An AsyncGeneratorRequest is a Record value used to store information about how an async generator should be resumed and contains capabilities for fulfilling or rejecting the corresponding promise.

They have the following fields:

@@ -39965,12 +40005,11 @@

AsyncGeneratorStart ( _generator_, _generatorBody_ )

1. Assert: If we return here, the async generator either threw an exception or performed either an implicit or explicit return. 1. Remove _genContext_ from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. 1. Set _generator_.[[AsyncGeneratorState]] to ~completed~. - 1. If _result_ is a normal completion, let _resultValue_ be *undefined*. - 1. Else, - 1. Let _resultValue_ be _result_.[[Value]]. - 1. If _result_.[[Type]] is not ~return~, then - 1. Return ! AsyncGeneratorReject(_generator_, _resultValue_). - 1. Return ! AsyncGeneratorResolve(_generator_, _resultValue_, *true*). + 1. If _result_.[[Type]] is ~normal~, set _result_ to NormalCompletion(*undefined*). + 1. If _result_.[[Type]] is ~return~, set _result_ to NormalCompletion(_result_.[[Value]]). + 1. Perform ! AsyncGeneratorCompleteStep(_generator_, _result_, *true*). + 1. Perform ! AsyncGeneratorDrainQueue(_generator_). + 1. Return *undefined*. 1. Set _generator_.[[AsyncGeneratorContext]] to _genContext_. 1. Set _generator_.[[AsyncGeneratorState]] to ~suspendedStart~. 1. Set _generator_.[[AsyncGeneratorQueue]] to a new empty List. @@ -39989,76 +40028,45 @@

AsyncGeneratorValidate ( _generator_, _generatorBrand_ )

- -

AsyncGeneratorResolve ( _generator_, _value_, _done_ )

-

The abstract operation AsyncGeneratorResolve takes arguments _generator_, _value_, and _done_ (a Boolean). It performs the following steps when called:

+ +

AsyncGeneratorEnqueue ( _generator_, _completion_, _promiseCapability_ )

+

The abstract operation AsyncGeneratorEnqueue takes arguments _generator_ (an AsyncGenerator), _completion_ (a Completion Record), and _promiseCapability_ (a PromiseCapability Record). It performs the following steps when called:

- 1. Assert: _generator_ is an AsyncGenerator instance. - 1. Let _queue_ be _generator_.[[AsyncGeneratorQueue]]. - 1. Assert: _queue_ is not an empty List. - 1. Let _next_ be the first element of _queue_. - 1. Remove the first element from _queue_. - 1. Let _promiseCapability_ be _next_.[[Capability]]. - 1. Let _iteratorResult_ be ! CreateIterResultObject(_value_, _done_). - 1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _iteratorResult_ »). - 1. Perform ! AsyncGeneratorResumeNext(_generator_). - 1. Return *undefined*. + 1. Let _request_ be AsyncGeneratorRequest { [[Completion]]: _completion_, [[Capability]]: _promiseCapability_ }. + 1. Append _request_ to the end of _generator_.[[AsyncGeneratorQueue]].
- -

AsyncGeneratorReject ( _generator_, _exception_ )

-

The abstract operation AsyncGeneratorReject takes arguments _generator_ and _exception_. It performs the following steps when called:

+ +

AsyncGeneratorCompleteStep ( _generator_, _completion_, _done_ [ , _realm_ ] )

+

The abstract operation AsyncGeneratorCompleteStep takes arguments _generator_ (an AsyncGenerator), _completion_ (a Completion Record), and _done_ (a Boolean) and optional argument _realm_ (a Realm Record). It performs the following steps when called:

- 1. Assert: _generator_ is an AsyncGenerator instance. 1. Let _queue_ be _generator_.[[AsyncGeneratorQueue]]. - 1. Assert: _queue_ is not an empty List. + 1. Assert: _queue_ is not empty. 1. Let _next_ be the first element of _queue_. 1. Remove the first element from _queue_. 1. Let _promiseCapability_ be _next_.[[Capability]]. - 1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « _exception_ »). - 1. Perform ! AsyncGeneratorResumeNext(_generator_). - 1. Return *undefined*. + 1. Let _value_ be _completion_.[[Value]]. + 1. If _completion_.[[Type]] is ~throw~, then + 1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « _value_ »). + 1. Else, + 1. Assert: _completion_.[[Type]] is ~normal~. + 1. If _realm_ is present, then + 1. Let _oldRealm_ be the running execution context's Realm. + 1. Set the running execution context's Realm to _realm_. + 1. Let _iteratorResult_ be ! CreateIterResultObject(_value_, _done_). + 1. Set the running execution context's Realm to _oldRealm_. + 1. Else, + 1. Let _iteratorResult_ be ! CreateIterResultObject(_value_, _done_). + 1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _iteratorResult_ »).
- -

AsyncGeneratorResumeNext ( _generator_ )

-

The abstract operation AsyncGeneratorResumeNext takes argument _generator_. It performs the following steps when called:

+ +

AsyncGeneratorResume ( _generator_, _completion_ )

+

The abstract operation AsyncGeneratorResume takes arguments _generator_ (an AsyncGenerator) and _completion_ (a Completion Record). It performs the following steps when called:

- 1. Assert: _generator_ is an AsyncGenerator instance. - 1. Let _state_ be _generator_.[[AsyncGeneratorState]]. - 1. Assert: _state_ is not ~executing~. - 1. If _state_ is ~awaiting-return~, return *undefined*. - 1. Let _queue_ be _generator_.[[AsyncGeneratorQueue]]. - 1. If _queue_ is an empty List, return *undefined*. - 1. Let _next_ be the value of the first element of _queue_. - 1. Assert: _next_ is an AsyncGeneratorRequest record. - 1. Let _completion_ be _next_.[[Completion]]. - 1. If _completion_ is an abrupt completion, then - 1. If _state_ is ~suspendedStart~, then - 1. Set _generator_.[[AsyncGeneratorState]] to ~completed~. - 1. Set _state_ to ~completed~. - 1. If _state_ is ~completed~, then - 1. If _completion_.[[Type]] is ~return~, then - 1. Set _generator_.[[AsyncGeneratorState]] to ~awaiting-return~. - 1. Let _promise_ be ? PromiseResolve(%Promise%, _completion_.[[Value]]). - 1. Let _fulfilledClosure_ be a new Abstract Closure with parameters (_value_) that captures _generator_ and performs the following steps when called: - 1. Set _generator_.[[AsyncGeneratorState]] to ~completed~. - 1. Return ! AsyncGeneratorResolve(_generator_, _value_, *true*). - 1. Let _onFulfilled_ be ! CreateBuiltinFunction(_fulfilledClosure_, 1, *""*, « »). - 1. Let _rejectedClosure_ be a new Abstract Closure with parameters (_reason_) that captures _generator_ and performs the following steps when called: - 1. Set _generator_.[[AsyncGeneratorState]] to ~completed~. - 1. Return ! AsyncGeneratorReject(_generator_, _reason_). - 1. Let _onRejected_ be ! CreateBuiltinFunction(_rejectedClosure_, 1, *""*, « »). - 1. Perform ! PerformPromiseThen(_promise_, _onFulfilled_, _onRejected_). - 1. Return *undefined*. - 1. Else, - 1. Assert: _completion_.[[Type]] is ~throw~. - 1. Perform ! AsyncGeneratorReject(_generator_, _completion_.[[Value]]). - 1. Return *undefined*. - 1. Else if _state_ is ~completed~, return ! AsyncGeneratorResolve(_generator_, *undefined*, *true*). - 1. Assert: _state_ is either ~suspendedStart~ or ~suspendedYield~. + 1. Assert: _generator_.[[AsyncGeneratorState]] is either ~suspendedStart~ or ~suspendedYield~. 1. Let _genContext_ be _generator_.[[AsyncGeneratorContext]]. 1. Let _callerContext_ be the running execution context. 1. Suspend _callerContext_. @@ -40067,27 +40075,18 @@

AsyncGeneratorResumeNext ( _generator_ )

1. Resume the suspended evaluation of _genContext_ using _completion_ as the result of the operation that suspended it. Let _result_ be the completion record returned by the resumed computation. 1. Assert: _result_ is never an abrupt completion. 1. Assert: When we return here, _genContext_ has already been removed from the execution context stack and _callerContext_ is the currently running execution context. - 1. Return *undefined*.
- -

AsyncGeneratorEnqueue ( _generator_, _completion_, _generatorBrand_ )

-

The abstract operation AsyncGeneratorEnqueue takes arguments _generator_, _completion_ (a Completion Record), and _generatorBrand_. It performs the following steps when called:

+ +

AsyncGeneratorUnwrapYieldResumption ( _resumptionValue_ )

+

The abstract operation AsyncGeneratorUnwrapYieldResumption takes argument _resumptionValue_ (a Completion Record). It performs the following steps when called:

- 1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%). - 1. Let _check_ be AsyncGeneratorValidate(_generator_, _generatorBrand_). - 1. If _check_ is an abrupt completion, then - 1. Let _badGeneratorError_ be a newly created *TypeError* object. - 1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « _badGeneratorError_ »). - 1. Return _promiseCapability_.[[Promise]]. - 1. Let _queue_ be _generator_.[[AsyncGeneratorQueue]]. - 1. Let _request_ be AsyncGeneratorRequest { [[Completion]]: _completion_, [[Capability]]: _promiseCapability_ }. - 1. Append _request_ to the end of _queue_. - 1. Let _state_ be _generator_.[[AsyncGeneratorState]]. - 1. If _state_ is not ~executing~, then - 1. Perform ! AsyncGeneratorResumeNext(_generator_). - 1. Return _promiseCapability_.[[Promise]]. + 1. If _resumptionValue_.[[Type]] is not ~return~, return Completion(_resumptionValue_). + 1. Let _awaited_ be Await(_resumptionValue_.[[Value]]). + 1. If _awaited_.[[Type]] is ~throw~, return Completion(_awaited_). + 1. Assert: _awaited_.[[Type]] is ~normal~. + 1. Return Completion { [[Type]]: ~return~, [[Value]]: _awaited_.[[Value]], [[Target]]: ~empty~ }.
@@ -40100,17 +40099,76 @@

AsyncGeneratorYield ( _value_ )

1. Let _generator_ be the value of the Generator component of _genContext_. 1. Assert: GetGeneratorKind() is ~async~. 1. Set _value_ to ? Await(_value_). - 1. Set _generator_.[[AsyncGeneratorState]] to ~suspendedYield~. - 1. Remove _genContext_ from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. - 1. Set the code evaluation state of _genContext_ such that when evaluation is resumed with a Completion _resumptionValue_ the following steps will be performed: - 1. If _resumptionValue_.[[Type]] is not ~return~, return Completion(_resumptionValue_). - 1. Let _awaited_ be Await(_resumptionValue_.[[Value]]). - 1. If _awaited_.[[Type]] is ~throw~, return Completion(_awaited_). - 1. Assert: _awaited_.[[Type]] is ~normal~. - 1. Return Completion { [[Type]]: ~return~, [[Value]]: _awaited_.[[Value]], [[Target]]: ~empty~ }. - 1. NOTE: When one of the above steps returns, it returns to the evaluation of the |YieldExpression| production that originally called this abstract operation. - 1. Return ! AsyncGeneratorResolve(_generator_, _value_, *false*). - 1. NOTE: This returns to the evaluation of the operation that had most previously resumed evaluation of _genContext_. + 1. Let _completion_ be NormalCompletion(_value_). + 1. Assert: The execution context stack has at least two elements. + 1. Let _previousContext_ be the second to top element of the execution context stack. + 1. Let _previousRealm_ be _previousContext_'s Realm. + 1. Perform ! AsyncGeneratorCompleteStep(_generator_, _completion_, *false*, _previousRealm_). + 1. Let _queue_ be _generator_.[[AsyncGeneratorQueue]]. + 1. If _queue_ is not empty, then + 1. NOTE: Execution continues without suspending the generator. + 1. Let _toYield_ be the first element of _queue_. + 1. Let _resumptionValue_ be _toYield_.[[Completion]]. + 1. Return AsyncGeneratorUnwrapYieldResumption(_resumptionValue_). + 1. Else, + 1. Set _generator_.[[AsyncGeneratorState]] to ~suspendedYield~. + 1. Remove _genContext_ from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. + 1. Set the code evaluation state of _genContext_ such that when evaluation is resumed with a Completion _resumptionValue_ the following steps will be performed: + 1. Return AsyncGeneratorUnwrapYieldResumption(_resumptionValue_). + 1. NOTE: When the above step returns, it returns to the evaluation of the |YieldExpression| production that originally called this abstract operation. + 1. Return *undefined*. + 1. NOTE: This returns to the evaluation of the operation that had most previously resumed evaluation of _genContext_. + +
+ + +

AsyncGeneratorAwaitReturn ( _generator_ )

+

The abstract operation AsyncGeneratorAwaitReturn takes argument _generator_ (an AsyncGenerator). It performs the following steps when called:

+ + 1. Let _queue_ be _generator_.[[AsyncGeneratorQueue]]. + 1. Assert: _queue_ is not empty. + 1. Let _next_ be the first element of _queue_. + 1. Let _completion_ be _next_.[[Completion]]. + 1. Assert: _completion_.[[Type]] is ~return~. + 1. Let _promise_ be ? PromiseResolve(%Promise%, _completion_.[[Value]]). + 1. Let _fulfilledClosure_ be a new Abstract Closure with parameters (_value_) that captures _generator_ and performs the following steps when called: + 1. Set _generator_.[[AsyncGeneratorState]] to ~completed~. + 1. Let _result_ be NormalCompletion(_value_). + 1. Perform ! AsyncGeneratorCompleteStep(_generator_, _result_, *true*). + 1. Perform ! AsyncGeneratorDrainQueue(_generator_). + 1. Return *undefined*. + 1. Let _onFulfilled_ be ! CreateBuiltinFunction(_fulfilledClosure_, 1, *""*, « »). + 1. Let _rejectedClosure_ be a new Abstract Closure with parameters (_reason_) that captures _generator_ and performs the following steps when called: + 1. Set _generator_.[[AsyncGeneratorState]] to ~completed~. + 1. Let _result_ be ThrowCompletion(_reason_). + 1. Perform ! AsyncGeneratorCompleteStep(_generator_, _result_, *true*). + 1. Perform ! AsyncGeneratorDrainQueue(_generator_). + 1. Return *undefined*. + 1. Let _onRejected_ be ! CreateBuiltinFunction(_rejectedClosure_, 1, *""*, « »). + 1. Perform ! PerformPromiseThen(_promise_, _onFulfilled_, _onRejected_). + +
+ + +

AsyncGeneratorDrainQueue ( _generator_ )

+

The abstract operation AsyncGeneratorDrainQueue takes argument _generator_ (an AsyncGenerator). It drains the generator's AsyncGeneratorQueue until it encounters an AsyncGeneratorRequest which holds a completion whose type is ~return~. It performs the following steps when called:

+ + 1. Assert: _generator_.[[AsyncGeneratorState]] is ~completed~. + 1. Let _queue_ be _generator_.[[AsyncGeneratorQueue]]. + 1. If _queue_ is empty, return. + 1. Let _done_ be *false*. + 1. Repeat, while _done_ is *false*, + 1. Let _next_ be the first element of _queue_. + 1. Let _completion_ be _next_.[[Completion]]. + 1. If _completion_.[[Type]] is ~return~, then + 1. Set _generator_.[[AsyncGeneratorState]] to ~awaiting-return~. + 1. Perform ! AsyncGeneratorAwaitReturn(_generator_). + 1. Set _done_ to *true*. + 1. Else, + 1. If _completion_.[[Type]] is ~normal~, then + 1. Set _completion_ to NormalCompletion(*undefined*). + 1. Perform ! AsyncGeneratorCompleteStep(_generator_, _completion_, *true*). + 1. If _queue_ is empty, set _done_ to *true*.
@@ -43052,7 +43110,7 @@

Additions and Changes That Introduce Incompatibilities with Prior Editions In ECMAScript 2015, the RegExp prototype object is not a RegExp instance. In previous editions it was a RegExp instance whose pattern is the empty String.

In ECMAScript 2015, *"source"*, *"global"*, *"ignoreCase"*, and *"multiline"* are accessor properties defined on the RegExp prototype object. In previous editions they were data properties defined on RegExp instances.

: In ECMAScript 2019, `Atomics.wake` has been renamed to `Atomics.notify` to prevent confusion with `Atomics.wait`.

-

, : In ECMAScript 2019, the number of Jobs enqueued by `await` was reduced, which could create an observable difference in resolution order between a `then()` call and an `await` expression.

+

, : In ECMAScript 2019, the number of Jobs enqueued by `await` was reduced, which could create an observable difference in resolution order between a `then()` call and an `await` expression.

Colophon