Stage 3 Draft / January 26, 2016

Async Functions

Introduction#

The introduction of promises and generators in ECMAScript presents an opportunity to dramatically improve the language-level model for writing asynchronous code in ECMAScript.

A similar proposal was made with Deferred Functions during ES6 discussions. The proposal here supports the same use cases, using similar or the same syntax, but directly building upon control flow structures parallel to those of generators, and using promises for the return type, instead of defining custom mechanisms.

Development of this proposal is happening at https://github.com/tc39/ecmascript-asyncawait. Please file issues there. Non-trivial contributions are limited to TC39 members but pull requests for minor issues are welcome and encouraged!

Status of this proposal#

This proposal was accepted into Stage 3 ("Candidate") of the ECMAScript spec process in September 2015. The champion intends for this proposal to be accepted into Stage 4 ("Finished") by the end of November.

Examples#

Take the following example, first written using Promises. This code chains a set of animations on an element, stopping when there is an exception in an animation, and returning the value produced by the final succesfully executed animation.


    function chainAnimationsPromise(elem, animations) {
        let ret = null;
        let p = currentPromise;
        for(const anim of animations) {
            p = p.then(function(val) {
                ret = val;
                return anim(elem);
            })
        }
        return p.catch(function(e) {
            /* ignore and keep going */
        }).then(function() {
            return ret;
        });
    }
    

Already with promises, the code is much improved from a straight callback style, where this sort of looping and exception handling is challenging.

Task.js and similar libraries offer a way to use generators to further simplify the code maintaining the same meaning:

    function chainAnimationsGenerator(elem, animations) {
        return spawn(function*() {
            let ret = null;
            try {
                for(const anim of animations) {
                    ret = yield anim(elem);
                }
            } catch(e) { /* ignore and keep going */ }
            return ret;
        });
    }
    

This is a marked improvement. All of the promise boilerplate above and beyond the semantic content of the code is removed, and the body of the inner function represents user intent. However, there is an outer layer of boilerplate to wrap the code in an additional generator function and pass it to a library to convert to a promise. This layer needs to be repeated in every function that uses this mechanism to produce a promise. This is so common in typical async Javascript code, that there is value in removing the need for the remaining boilerplate.

With async functions, all the remaining boilerplate is removed, leaving only the semantically meaningful code in the program text:


    async function chainAnimationsAsync(elem, animations) {
        let ret = null;
        try {
            for(const anim of animations) {
                ret = await anim(elem);
            }
        } catch(e) { /* ignore and keep going */ }
        return ret;
    }
    

1Async Function Definitions#

Syntax

AsyncFunctionDeclaration[Yield, Await, Default]::async[no LineTerminator here]functionBindingIdentifier[?Yield, ?Await](FormalParameters[Await]){AsyncFunctionBody} [+Default]async[no LineTerminator here]function(FormalParameters[Await]){AsyncFunctionBody} AsyncFunctionExpression::async[no LineTerminator here]function(FormalParameters[Await]){AsyncFunctionBody} async[no LineTerminator here]functionBindingIdentifier[Await](FormalParameters[Await]){AsyncFunctionBody} AsyncMethod[Yield, Await]::async[no LineTerminator here]PropertyName[?Yield, ?Await](StrictFormalParameters[Await]){AsyncFunctionBody} AsyncFunctionBody::FunctionBody[Await] AwaitExpression[Yield]::awaitUnaryExpression[?Yield, Await] AsyncArrowFunction[In, Yield, Await]::async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In] CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In] AsyncConciseBody[In]::[lookahead ≠ {]AssignmentExpression[?In, Await] {AsyncFunctionBody} AsyncArrowBindingIdentifier[Yield]:BindingIdentifier[?Yield, Await] CoverCallExpressionAndAsyncArrowHead[Yield, Await]::MemberExpression[?Yield, ?Await]Arguments[?Yield, ?Await] Note 1yield is Identifier or YieldExpression in FunctionParameters of AsyncFunctionDeclaration and AsyncFunctionExpression and ArrowParameters of AsyncArrowFunction depending on context. await is always parsed as an AwaitExpression but is a Syntax Error via static semantics. yield is always allowed as an identifier of an AsyncFunctionExpression. Note 2Unlike YieldExpression, it is a Syntax Error to omit the operand of an AwaitExpression. You must await something.

Supplemental Syntax

When processing the production AsyncArrowFunction[In, Yield, Await]::CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In] the interpretation of CoverCallExpressionAndAsyncArrowHead is refined using the following gramar:

AsyncArrowHead::async[no LineTerminator here]ArrowFormalParameters[Await]

1.1Async Function Declarations & Expressions#

1.1.1Static Semantics: Early Errors#

AsyncFunctionDeclaration[Yield, Await, Default]::async[no LineTerminator here]functionBindingIdentifier[?Yield, ?Await](FormalParameters[Await]){AsyncFunctionBody}
AsyncFunctionDeclaration[Yield, Await, Default]::[+Default]async[no LineTerminator here]function(FormalParameters[Await]){AsyncFunctionBody}
AsyncFunctionExpression::async[no LineTerminator here]function(FormalParameters[Await]){AsyncFunctionBody}
AsyncFunctionExpression::async[no LineTerminator here]functionBindingIdentifier[Await](FormalParameters[Await]){AsyncFunctionBody}

  • It is a Syntax Error if ContainsUseStrict of AsyncFunctionBody is true and IsSimpleParameterList of FormalParameters is false.
  • It is a Syntax Error if FormalParameters Contains AwaitExpression is true.
  • If the source code matching this production is strict code, the Early Error rules for StrictFormalParameters : FormalParameters are applied.
  • If the source code matching this production is strict code, it is a Syntax Error if BindingIdentifier is the IdentifierName eval or the IdentifierName arguments.
  • It is a Syntax Error if any element of the BoundNames of FormalParameters also occurs in the LexicallyDeclaredNames of AsyncFunctionBody.
  • It is a Syntax Error if FormalParameters contains SuperProperty is true.
  • It is a Syntax Error if AsyncFunctionBody contains SuperProperty is true.
  • It is a Syntax Error if FormalParameters contains SuperCall is true.
  • It is a Syntax Error if AsyncFunctionBody contains SuperCall is true.

1.1.2Static Semantics: BoundNames#

AsyncFunctionDeclaration[Yield, Await, Default]::async[no LineTerminator here]functionBindingIdentifier[?Yield, ?Await](FormalParameters[Await]){AsyncFunctionBody}

  1. Return the BoundNames of BindingIdentifier.

AsyncFunctionDeclaration[Yield, Await, Default]::[+Default]async[no LineTerminator here]function(FormalParameters[Await]){AsyncFunctionBody}

  1. Return «"*default*"».
Note"*default*" is used within this specification as a synthetic name for hoistable anonymous functions that are defined using export declarations.

1.1.3Static Semantics: Contains#

With parameter symbol.

AsyncFunctionDeclaration[Yield, Await, Default]::async[no LineTerminator here]functionBindingIdentifier[?Yield, ?Await](FormalParameters[Await]){AsyncFunctionBody}
AsyncFunctionDeclaration[Yield, Await, Default]::[+Default]async[no LineTerminator here]function(FormalParameters[Await]){AsyncFunctionBody}
AsyncFunctionExpression::async[no LineTerminator here]function(FormalParameters[Await]){AsyncFunctionBody}
AsyncFunctionExpression::async[no LineTerminator here]functionBindingIdentifier[Await](FormalParameters[Await]){AsyncFunctionBody}

  1. Return false.
NoteCould probably check if symbol is one of these declarations or expressions, but this is not done in current spec and may not be necessary. See also: https://bugs.ecmascript.org/show_bug.cgi?id=4383.

1.1.4Static Semantics: HasName#

AsyncFunctionExpression::async[no LineTerminator here]function(FormalParameters[Await]){AsyncFunctionBody}

  1. Return false.

AsyncFunctionExpression::async[no LineTerminator here]functionBindingIdentifier[Await](FormalParameters[Await]){AsyncFunctionBody}

  1. Return true.

1.1.5Static Semantics: IsConstantDeclaration#

AsyncFunctionDeclaration[Yield, Await, Default]::async[no LineTerminator here]functionBindingIdentifier[?Yield, ?Await](FormalParameters[Await]){AsyncFunctionBody}
AsyncFunctionDeclaration[Yield, Await, Default]::[+Default]async[no LineTerminator here]function(FormalParameters[Await]){AsyncFunctionBody}

  1. Return false.

1.1.6Static Semantics: IsFunctionDefinition#

AsyncFunctionExpression::async[no LineTerminator here]function(FormalParameters[Await]){AsyncFunctionBody}
AsyncFunctionExpression::async[no LineTerminator here]functionBindingIdentifier[Await](FormalParameters[Await]){AsyncFunctionBody}

  1. Return true.

1.1.7Runtime Semantics: InstantiateFunctionObject#

With parameter scope.

AsyncFunctionDeclaration[Yield, Await, Default]::async[no LineTerminator here]functionBindingIdentifier[?Yield, ?Await](FormalParameters[Await]){AsyncFunctionBody}

  1. If the function code for AsyncFunctionDeclaration is strict mode code, let strict be true. Otherwise, let strict be false.
  2. Let name be StringValue of BindingIdentifier
  3. Let F be AsyncFunctionCreate(Normal, FormalParameters, AsyncFunctionBody, scope, strict).
  4. Perform SetFunctionName(F, name).
  5. Return F.

AsyncFunctionDeclaration[Yield, Await, Default]::[+Default]async[no LineTerminator here]function(FormalParameters[Await]){AsyncFunctionBody}

  1. If the function code for AsyncFunctionDeclaration is strict mode code, let strict be true. Otherwise, let strict be false.
  2. Let F be AsyncFunctionCreate(Normal, FormalParameters, AsyncFunctionBody, scope, strict).
  3. Perform SetFunctionName(F, "default").
  4. Return F.

1.1.8Runtime Semantics: EvaluateBody#

With parameters functionObject and List argumentsList.

AsyncFunctionBody::FunctionBody[Await]

  1. Let promiseCapability be NewPromiseCapability(%Promise%).
  2. Let declResult be FunctionDeclarationInstantiation(functionObject, argumentsList).
  3. If declResult is not an abrupt completion, then
    1. Perform ! AsyncFunctionStart(promiseCapability, FunctionBody).
  4. Else declResult is an abrupt completion
    1. Perform ! Call(promiseCapability.[[Reject]], undefined, «declResult.[[value]]»).
  5. Return Completion{[[type]]: return, [[value]]: promiseCapability.[[Promise]], [[target]]: empty}.

1.1.9Runtime Semantics: Evaluation#

AsyncFunctionDeclaration[Yield, Await, Default]::async[no LineTerminator here]functionBindingIdentifier[?Yield, ?Await](FormalParameters[Await]){AsyncFunctionBody}

  1. Return NormalCompletion(empty).

AsyncFunctionDeclaration[Yield, Await, Default]::[+Default]async[no LineTerminator here]function(FormalParameters[Await]){AsyncFunctionBody}

  1. Return NormalCompletion(empty).

AsyncFunctionExpression::async[no LineTerminator here]function(FormalParameters[Await]){AsyncFunctionBody}

  1. If the function code for AsyncFunctionExpression is strict mode code, let strict be true. Otherwise let strict be false.
  2. Let scope be the LexicalEnvironment of the running execution context.
  3. Let closure be AsyncFunctionCreate(Normal, FormalParameters, AsyncFunctionBody, scope, strict).
  4. Return closure.

AsyncFunctionExpression::async[no LineTerminator here]functionBindingIdentifier[Await](FormalParameters[Await]){AsyncFunctionBody}

  1. If the function code for AsyncFunctionExpression is strict mode code, let strict be true. Otherwise let strict be false.
  2. Let scope be the LexicalEnvironment of the running execution context.
  3. Let funcEnv be NewDeclarativeEnvironment(scope).
  4. Let envRec be funcEnv's EnvironmentRecord.
  5. Let name be StringValue of BindingIdentifier.
  6. Perform envRec.CreateImmutableBinding( name).
  7. Let closure be AsyncFunctionCreate(Normal, FormalParameters, AsyncFunctionBody, funcEnv, strict).
  8. Perform SetFunctionName(closure, name).
  9. Perform envRec.InitializeBinding( name, closure).
  10. Return closure.

AwaitExpression[Yield]::awaitUnaryExpression[?Yield, Await]

  1. Let exprRef be the result of evaluating UnaryExpression.
  2. Let value be ? GetValue(exprRef).
  3. Return AsyncFunctionAwait(value).

1.2Async Arrow Functions#

1.2.1Static Semantics: Early Errors#

AsyncArrowFunction[In, Yield, Await]::async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In]

  • It is a Syntax Error if ContainsUseStrict of AsyncConciseBody is true and IsSimpleParameterList of ArrowParameters is false.
  • It is a Syntax Error if any element of the BoundNames of AsyncArrowBindingIdentifier also occurs in the LexicallyDeclaredNames of AsyncConciseBody.

AsyncArrowFunction[In, Yield, Await]::CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In]

  • It is a Syntax Error if CoverCallExpressionAndAsyncArrowHead Contains YieldExpression is true.
  • It is a Syntax Error if CoverCallExpressionAndAsyncArrowHead Contains AwaitExpression is true.
  • It is a Syntax Error if any element of the BoundNames of CoverCallExpressionAndAsyncArrowHead also occurs in the LexicallyDeclaredNames of AsyncConciseBody.
  • It is a Syntax Error if the lexical token sequence matched by CoverCallExpressionAndAsyncArrowHead cannot be parsed with no tokens left over using AsyncArrowHead as the goal symbol.
  • All Early Error rules for AsyncArrowHead and its derived productions apply to CoveredAsyncArrowHead of CoverCallExpressionAndAsyncArrowHead.

1.2.2Static Semantics: CoveredAsyncArrowHead#

CoverCallExpressionAndAsyncArrowHead[Yield, Await]::MemberExpression[?Yield, ?Await]Arguments[?Yield, ?Await]

  1. Return the result of parsing the lexical token stream matched by CoverCallExpressionAndAsyncArrowHead[Yield, Await] using AsyncArrowHead as the goal symbol.

1.2.3Static Semantics: BoundNames#

CoverCallExpressionAndAsyncArrowHead[Yield, Await]::MemberExpression[?Yield, ?Await]Arguments[?Yield, ?Await]

  1. Let head be CoveredAsyncFunctionHead of CoverCallExpressionAndAsyncArrowHead.
  2. Return the BoundNames of head.

1.2.4Static Semantics: Contains#

With parameter symbol.

AsyncArrowFunction[In, Yield, Await]::async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In]

  1. If symbol is not one of NewTarget, SuperProperty, SuperCall, super, or this, return false.
  2. Return AsyncConciseBody contains symbol.

AsyncArrowFunction[In, Yield, Await]::CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In]

  1. If symbol is not one of NewTarget, SuperProperty, SuperCall, super, or this, return false.
  2. Let head be CoveredAsyncFunctionHead of CoverCallExpressionAndAsyncArrowHead.
  3. If head Contains symbol is true, return true.
  4. Return AsyncConciseBody Contains symbol.
NoteNormally, Contains does not look inside most function forms. However, Contains is used to detect new.target, this, and super usage within an AsyncArrowFunction.

1.2.5Static Semantics: ContainsExpression#

AsyncArrowBindingIdentifier[Yield]:BindingIdentifier[?Yield, Await]

  1. Return false.

1.2.6Static Semantics: ExpectedArgumentCount#

AsyncArrowBindingIdentifier[Yield]:BindingIdentifier[?Yield, Await]

  1. Return 1.

1.2.7Static Semantics: HasName#

AsyncArrowFunction[In, Yield, Await]::async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In]

AsyncArrowFunction[In, Yield, Await]::CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In]

  1. Return false.

1.2.8Static Semantics: IsSimpleParameterList#

AsyncArrowBindingIdentifier[Yield]:BindingIdentifier[?Yield, Await]

  1. Return true.

1.2.9Static Semantics: LexicallyDeclaredNames#

AsyncConciseBody[In]::[lookahead ≠ {]AssignmentExpression[?In, Await]

  1. Return an empty List.

1.2.10Static Semantics: LexicallyScopedDeclarations#

AsyncConciseBody[In]::[lookahead ≠ {]AssignmentExpression[?In, Await]

  1. Return an empty List.

1.2.11Static Semantics: VarDeclaredNames#

AsyncConciseBody[In]::[lookahead ≠ {]AssignmentExpression[?In, Await]

  1. Return an empty List.

1.2.12Static Semantics: VarScopedDeclarations#

AsyncConciseBody[In]::[lookahead ≠ {]AssignmentExpression[?In, Await]

  1. Return an empty List.

1.2.13Runtime Semantics: IteratorBindingInitialization#

With parameters iteratorRecord and environment.

AsyncArrowBindingIdentifier[Yield]:BindingIdentifier[?Yield, Await]

  1. Assert: iteratorRecord.[[done]] is false.
  2. Let next be IteratorStep(iteratorRecord.[[iterator]]).
  3. If next is an abrupt completion, set iteratorRecord.[[done]] to true.
  4. ReturnIfAbrupt(next).
  5. If next is false, set iteratorRecord.[[done]] to true.
  6. Else
    1. Let v be IteratorValue(next).
    2. If v is an abrupt completion, set iteratorRecord.[[done]] to true.
    3. ReturnIfAbrupt(v).
  7. If iteratorRecord.[[done]] is true, let v be undefined.
  8. Return the result of performing BindingInitialization for BindingIdentifier using v and environment as the arguments.

1.2.14Runtime Semantics: EvaluateBody#

With parameter functionObject and List argumentsList.

AsyncConciseBody[In]::[lookahead ≠ {]AssignmentExpression[?In, Await]

  1. Let promiseCapability be NewPromiseCapability(%Promise%).
  2. Let declResult be FunctionDeclarationInstantiation(functionObject, argumentsList).
  3. If declResult is not an abrupt completion, then
    1. Perform ! AsyncFunctionStart(promiseCapability, AssignmentExpression).
  4. Else declResult is an abrupt completion
    1. Perform ! Call(promiseCapability.[[Reject]], undefined, «declResult.[[value]]»).
  5. Return Completion{[[type]]: return, [[value]]: promiseCapability.[[Promise]], [[target]]: empty}.

AsyncConciseBody[In]::{AsyncFunctionBody}

  1. Return the result of calling EvaluateBody of AsyncFunctionBody passing functionObject and argumentsList as the arguments.

1.2.15Runtime Semantics: Evaluation#

AsyncArrowFunction[In, Yield, Await]::async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In]

  1. If the function code for this AsyncArrowFunction is strict mode code, let strict be true. Otherwise, let strict be false.
  2. Let scope be the LexicalEnvironment of the running execution context.
  3. Let parameters be AsyncArrowBindingIdentifier.
  4. Let closure be AsyncFunctionCreate(Arrow, parameters, AsyncConciseBody, scope, strict).
  5. Return closure.

AsyncArrowFunction[In, Yield, Await]::CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In]

  1. If the function code for this AsyncArrowFunction is strict mode code, let strict be true. Otherwise, let strict be false.
  2. Let scope be the LexicalEnvironment of the running execution context.
  3. Let head be CoveredAsyncArrowHead of CoverCallExpressionAndAsyncArrowHead.
  4. Let parameters be the ArrowFormalParameters production matched by head.
  5. Let closure be AsyncFunctionCreate(Arrow, parameters, AsyncConciseBody, scope, strict).
  6. Return closure.

1.3Async Methods#

1.3.1Static Semantics: Early Errors#

AsyncMethod[Yield, Await]::async[no LineTerminator here]PropertyName[?Yield, ?Await](StrictFormalParameters[Await]){AsyncFunctionBody}

  • It is a Syntax Error if ContainsUseStrict of AsyncFunctionBody is true and IsSimpleParameterList of StrictFormalParameters is false.
  • It is a Syntax Error if HasDirectSuper of AsyncMethod is true.
  • It is a Syntax Error if StrictFormalParameters Contains AwaitExpression is true.
  • It is a Syntax Error if any element of the BoundNames of StrictFormalParameters also occurs in the LexicallyDeclaredNames of AsyncFunctionBody

1.3.2Static Semantics: ComputedPropertyContains#

With parameter symbol.

AsyncMethod[Yield, Await]::async[no LineTerminator here]PropertyName[?Yield, ?Await](StrictFormalParameters[Await]){AsyncFunctionBody}

  1. Return the result of ComputedPropertyContains for PropertyName with argument symbol.

1.3.3Static Semantics: HasComputedPropertyKey#

AsyncMethod[Yield, Await]::async[no LineTerminator here]PropertyName[?Yield, ?Await](StrictFormalParameters[Await]){AsyncFunctionBody}

  1. Return IsComputedPropertyKey of PropertyName.

1.3.4Static Semantics: HasDirectSuper#

AsyncMethod[Yield, Await]::async[no LineTerminator here]PropertyName[?Yield, ?Await](StrictFormalParameters[Await]){AsyncFunctionBody}

  1. If StrictFormalParameters Contains SuperCall is true, return true.
  2. Return AsyncFunctionBody Contains SuperCall.

1.3.5Static Semantics: PropName#

AsyncMethod[Yield, Await]::async[no LineTerminator here]PropertyName[?Yield, ?Await](StrictFormalParameters[Await]){AsyncFunctionBody}

  1. Return PropName of PropertyName.

1.3.6Runtime Semantics: PropertyDefinitionEvaluation#

With parameters object and enumerable.

AsyncMethod[Yield, Await]::async[no LineTerminator here]PropertyName[?Yield, ?Await](StrictFormalParameters[Await]){AsyncFunctionBody}

  1. Let propKey be the result of evaluating PropertyName.
  2. ReturnIfAbrupt(propKey).
  3. If the function code for this AsyncMethod is strict mode code, let strict be true. Otherwise let strict be false.
  4. Let scope be the LexicalEnvironment of the running execution context.
  5. Let closure be AsyncFunctionCreate(Method, StrictFormalParameters, AsyncFunctionBody, scope, strict).
  6. Perform ! MakeMethod(closure, object).
  7. Perform ! SetFunctionName(closure, propKey).
  8. Let desc be the Property Descriptor{[[Value]]: closure, [[Writable]]: true, [[Enumerable]]: enumerable, [[Configurable]]: true}.
  9. Return ? DefinePropertyOrThrow(object, propKey, desc).

2Abstract Operations#

2.1AsyncFunctionCreate ( kind, ParameterList, Body, Scope, Strict )#

The abstract operation AsyncFunctionCreate requires the arguments: kind which is one of (Normal, Method, Arrow), a parameter list production specified by ParameterList, a body production specified by Body, a Lexical Environment specified by Scope, and a Boolean flag Strict. AsyncFunctionCreate performs the following steps:

  1. Let functionPrototype be the intrinsic object %AsyncFunctionPrototype%.
  2. Let F be ! FunctionAllocate(functionPrototype, Strict, "non-constructor").
  3. Return ! FunctionInitialize(F, kind, ParameterList, Body, Scope).

2.2AsyncFunctionStart ( promiseCapability, asyncFunctionBody )#

  1. Let runningContext be the running execution context.
  2. Let asyncContext be a copy of runningContext.
  3. Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution context the following steps will be performed:
    1. Let result be the result of evaluating asyncFunctionBody.
    2. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done.
    3. Remove asyncContext 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.
    4. If result.[[type]] is normal, then
      1. Perform ! Call(promiseCapability.[[Resolve]], undefined, «undefined»).
    5. Else if result.[[type]] is return, then
      1. Perform ! Call(promiseCapability.[[Resolve]], undefined, «result.[[value]]»).
    6. Else,
      1. Assert: result.[[type]] is throw.
      2. Perform ! Call(promiseCapability.[[Reject]], undefined, «result.[[value]]»).
    7. Return.
  4. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
  5. Resume the suspended evaluation of asyncContext. Let result be the value returned by the resumed computation.
  6. Assert: When we return here, asyncContext has already been removed from the execution context stack and runningContext is the currently running execution context.
  7. Assert: result is a normal completion with a value of undefined. The possible sources of completion values are AsyncFunctionAwait or, if the async function doesn't await anything, the step 3.7 above.
  8. Return.

2.3AsyncFunctionAwait ( value )#

  1. Let asyncContext be the running execution context.
  2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  3. Let resolveResult be ! Call(promiseCapability.[[Resolve]], undefined, « value »).
  4. Let onFulfilled be a new built-in function object as defined in AsyncFunction Awaited Fulfilled.
  5. Let onRejected be a new built-in function object as defined in AsyncFunction Awaited Rejected.
  6. Set onFulfilled and onRejected's [[AsyncContext]] internal slots to asyncContext.
  7. Let throwawayCapability be NewPromiseCapability(%Promise%).
  8. Perform ! PerformPromiseThen(promiseCapability.[[Promise]], onFulfilled, onRejected, throwawayCapability).
  9. Remove asyncContext 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.
  10. Set the code evaluation state of asyncContext such that when evaluation is resumed with a Completion resumptionValue the following steps will be performed:
    1. Return resumptionValue.
  11. Return.
NoteThe return value of this abstract operation is unused. The interesting return is that of resumptionValue being returned to the AwaitExpression production that originally called this abstract operation.

2.4AsyncFunction Awaited Fulfilled#

Function F is called with the parameter value.

  1. Let asyncContext be the value of F's [[AsyncContext]] internal slot.
  2. Let prevContext be the running execution context.
  3. Suspend prevContext.
  4. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
  5. Resume the suspended evaluation of asyncContext using NormalCompletion(value) as the result of the operation that suspended it. Let result be the value returned by the resumed computation.
  6. Assert: When we reach this step, asyncContext has already been removed from the execution context stack and prevContext is the currently running execution context.
  7. Return Completion(result).

2.5AsyncFunction Awaited Rejected#

Function F is called with the parameter reason.

  1. Let asyncContext be the value of F's [[AsyncContext]] internal slot.
  2. Let prevContext be the running execution context.
  3. Suspend prevContext.
  4. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
  5. Resume the suspended evaluation of asyncContext using Completion{[[type]]: throw, [[value]]: reason, [[target]]: empty} as the result of the operation that suspended it. Let result be the value returned by the resumed computation.
  6. Assert: When we reach this step, asyncContext has already been removed from the execution context stack and prevContext is the currently running execution context.
  7. Return Completion(result).

3Async Function Objects#

3.1The Async Function Constructor#

The AsyncFunction Constructor is the %AsyncFunction% intrinsic object and is a subclass of Function. When AsyncFunction is called as a function rather than as a constructor, it creates and initializes a new AsyncFunction object. Thus the function call AsyncFunction(…) is equivalent to the object creation expression new AsyncFunction(…) with the same arguments.

The AsyncFunction constructor is designed to be subclassable. It may be used as the value of an extends clause of a class definition. Subclass constructors that intend to inherit the specified AsyncFunction behaviour must include a super call to the AsyncFunction constructor to create and initialize a subclass instances with the internal slots necessary for built-in async function behaviour.

3.1.1AsyncFunction(p1, p2, ..., pn, body)#

The last argument specifies the body (executable code) of an async function. Any preceding arguments specify formal parameters.

When the AsyncFunction function is called with some arguments p1, p2, ..., pn, body (where n might be 0, that is, there are no p arguments, and where body might also not be provided), the following steps are taken:

  1. Let C be the active function object.
  2. Let args be the argumentsList that was passed to this function by [[Call]] or [[Construct]].
  3. Return CreateDynamicFunction(C, NewTarget, "async", args).
NoteSee note for 19.2.1.1

3.2Properties of the AsyncFunction constructor#

The AsyncFunction constructor is a standard built-in function object that inherits from the Function constructor. The value of the [[Prototype]] internal slot of the AsyncFunction constructor is the intrinsic object %Function%.

The value of the [[Extensible]] internal slot of the AsyncFunction constructor is true.

The value of the name property of the AsyncFunction is "AsyncFunction".

The AsyncFunction constructor has the following properties:

3.2.1AsyncFunction.length#

This is a data property with a value of 1. This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.

3.2.2AsyncFunction.prototype#

The intitial value of AsyncFunction.prototype is the intrinsic object %AsyncFunctionPrototype%.

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.

3.3Properties of the AsyncFunction Prototype Object#

The AsyncFunction prototype object is an ordinary object. In addition to being the value of the prototype property of the %AsyncFunction% intrinsic, it is the %AsyncFunctionPrototype% intrinsic.

The value of the [[Prototype]] internal slot of the AsyncFunction prototype object is the %FunctionPrototype% intrinsic object. The initial value of the [[Extensible]] internal slot of the AsyncFunction prototype object is true.

The AsyncFunction prototype object does not have a prototype property.

NotePresumably this could be Promise.prototype but I'm not sure this has any value?

3.3.1AsyncFunction.prototype.constructor#

The initial value of AsyncFunction.prototype.constructor is the intrinsic object %AsyncFunction%

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.

3.3.2AsyncFunction.prototype[@@toStringTag]#

The initial value of the @@toStringTag property is the string value "AsyncFunction".

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.

3.4AsyncFunction Instances#

Every AsyncFunction instance is an ECMAScript function object and has the internal slots listed in Table 27. The value of the [[FunctionKind]] internal slot for all such instances is "normal". AsyncFunction instances are not constructors and do not have a [[Construct]] internal slot. AsyncFunction instances do not have a prototype property as they are not constructable.

Each AsyncFunction instance has the following own properties:

3.4.1length#

The value of the length property is an integer that indicates the typical number of arguments expected by the AsyncFunction. However, the language permits the function to be invoked with some other number of arguments. The behaviour of an AsyncFunction when invoked on a number of arguments other than the number specified by its length property depends on the function.

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.

3.4.2name#

The specification for the name property of Function instances given in 19.2.4.2 also applies to AsyncFunction instances.

4Modified Abstract Operations#

4.1OrdinaryCallEvaluateBody#

When the abstract operation OrdinaryCallEvaluateBody is called with function object F and List argumentsList the following steps are taken:

  1. Let status be FunctionDeclarationInstantiation(F, argumentsList).
  2. ReturnIfAbrupt(status)
  3. Return the result of EvaluateBody of the parsed code that is the value of F's [[ECMAScriptCode]] internal slot passing F as the argument.
Note The call to FunctionDeclarationInstantiation has moved to the EvaluateBody step of all the EvaluateBody steps. Async functions have special handling.

4.2Runtime Semantics: EvaluateBody#

With parameters functionObject and List argumentsList.

NoteThis clause redefines EvaluateBody for Function Declaration and Expression bodies. FunctionBody::FunctionStatementList
  1. Let status be FunctionDeclarationInstantiation(functionObject, argumentsList).
  2. ReturnIfAbrupt(status)
  3. Return the result of evaluating FunctionStatementList.

4.3Runtime Semantics: EvaluateBody#

With parameters functionObject and List argumentsList.

NoteThis clause redefines EvaluateBody for arrow function concise bodies. ConciseBody::AssignmentExpression
  1. Let status be FunctionDeclarationInstantiation(functionObject, argumentsList).
  2. ReturnIfAbrupt(status)
  3. Let exprRef be the result of evaluating AssignmentExpression.
  4. Let exprValue be GetValue(exprRef).
  5. ReturnIfAbrupt(exprValue).
  6. Return Completion{[[type]]: return, [[value]]: exprValue, [[target]]: empty}.

4.4Runtime Semantics: EvaluateBody#

With parameters functionObject and List argumentsList.

NoteThis clause redefines EvaluateBody for generator function bodies. GeneratorBody::FunctionBody
  1. Let status be FunctionDeclarationInstantiation(functionObject, argumentsList).
  2. ReturnIfAbrupt(status)
  3. Let G be OrdinaryCreateFromConstructor(functionObject, "%GeneratorPrototype%", «[[GeneratorState]], [[GeneratorContext]]» ).
  4. ReturnIfAbrupt(G).
  5. Perform GeneratorStart(G, FunctionBody).
  6. Return Completion{[[type]]: return, [[value]]: G, [[target]]: empty}.

5Modified Productions#

Unless otherwise defined in this section, every production that includes a Yield parameter is modified to include an Await parameter and any Non-terminal with a ?Yield parameter is modified to include an ?Await parameter.

5.1HoistableDeclaration#

HoistableDeclaration[Yield, Default, Await]:FunctionDeclaration[?Yield, ?Default, ?Await] GeneratorDeclaration[?Yield, ?Default, ?Await] AsyncFunctionDeclaration[?Yield, ?Default, ?Await]

5.2ArrowFunction#

ArrowFunction[Yield, In, Await]:ArrowParameters[?Yield, ?Await][no LineTerminator here]=>ConciseBody[?In]

5.2.1Static Semantics: Early Errors#

ArrowFunction[Yield, In, Await]:ArrowParameters[?Yield, ?Await][no LineTerminator here]=>ConciseBody[?In]

  • It is a Syntax Error if ArrowParameters Contains YieldExpression is true.
  • It is a Syntax Error if ArrowParameters Contains AwaitExpression is true.
  • It is a Syntax Error if any element of the BoundNames of ArrowParameters also occurs in the LexicallyDeclaredNames of ConciseBody.

5.3Identifiers#

IdentifierReference[Yield, Await]:Identifier [~Yield]yield [~Await]await BindingIdentifier[Yield, Await]:Identifier [~Yield]yield [~Await]await LabelIdentifier[Yield, Await]:Identifier [~Yield]yield [~Await]await

5.3.1Static Semantics: Early Errors#

IdentifierReference[Yield, Await]:Identifier
BindingIdentifier[Yield, Await]:Identifier
LabelIdentifier[Yield, Await]:Identifier

  • It is a Syntax Error if this production has a [Yield] parameter and StringValue of Identifier is "yield".
  • It is a Syntax Error if this production has an [Await] parameter and StringValue of Identifier is "await".

5.4PrimaryExpression#

PrimaryExpression[Yield, Await]:this IdentifierReference[?Yield, ?Await] Literal ArrayLiteral[?Yield, ?Await] ObjectLiteral[?Yield, ?Await] FunctionExpression ClassExpression[?Yield, ?Await] GeneratorExpression AsyncFunctionExpression RegularExpressionLiteral TemplateLiteral[?Yield, ?Await] CoverParenthesizedExpressionAndArrowParameterList[?Yield, ?Await]

5.5CallExpression#

CallExpression[Yield, Await]: CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await] MemberExpression[?Yield, ?Await]Arguments[?Yield, ?Await] SuperCall[?Yield, ?Await] CallExpression[?Yield, ?Await]Arguments[?Yield, ?Await] CallExpression[?Yield, ?Await][Expression[In, ?Yield, ?Await]] CallExpression[?Yield, ?Await].IdentifierName CallExpression[?Yield, ?Await]TemplateLiteral[?Yield, ?Await]

5.6UnaryExpression#

UnaryExpression[Yield, Await]:PostfixExpression[?Yield, ?Await] deleteUnaryExpression[?Yield, ?Await] voidUnaryExpression[?Yield, ?Await] typeofUnaryExpression[?Yield, ?Await] ++UnaryExpression[?Yield, ?Await] --UnaryExpression[?Yield, ?Await] +UnaryExpression[?Yield, ?Await] -UnaryExpression[?Yield, ?Await] ~UnaryExpression[?Yield, ?Await] !UnaryExpression[?Yield, ?Await] [+Await]AwaitExpression[?Yield] NoteCurrently it is not possible for an AwaitExpression and a YieldExpression to be parsed together, so technically passing the ?Yield parameter for UnaryExpression:[+Await]awaitUnaryExpression[?Yield, Await] is unnecessary. It will probably be useful for AsyncGenerators however.

5.6.1Static Semantics: IsFunctionDefinition#

UnaryExpression[Yield, Await]:deleteUnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:voidUnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:typeofUnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:++UnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:--UnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:+UnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:-UnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:~UnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:!UnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:[+Await]AwaitExpression[?Yield]

  1. Return false.

5.6.2Static Semantics: IsValidSimpleAssignmentTarget#

UnaryExpression[Yield, Await]:deleteUnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:voidUnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:typeofUnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:++UnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:--UnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:+UnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:-UnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:~UnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:!UnaryExpression[?Yield, ?Await]
UnaryExpression[Yield, Await]:[+Await]AwaitExpression[?Yield]

  1. Return false.

5.7MethodDefinition#

MethodDefinition[Yield, Await]:PropertyName[?Yield, ?Await](StrictFormalParameters){FunctionBody} GeneratorMethod[?Yield, ?Await] AsyncMethod[?Yield, ?Await] getPropertyName[?Yield, ?Await](){FunctionBody} setPropertyName[?Yield, ?Await](PropertySetParameterList){FunctionBody}

5.8AssignmentExpression#

AssignmentExpression[In, Yield, Await]:ConditionalExpression[?In, ?Yield, ?Await] [+Yield]YieldExpression[?In] ArrowFunction[?In, ?Yield, ?Await] AsyncArrowFunction[?In, ?Yield, ?Await] LeftHandSideExpression[?Yield, ?Await]=AssignmentExpression[?In, ?Yield, ?Await] LeftHandSideExpression[?Yield, ?Await]AssignmentOperatorAssignmentExpression[?In, ?Yield, ?Await]

5.8.1Static Semantics: IsFunctionDefinition#

AssignmentExpression[In, Yield, Await]:ArrowFunction[?In, ?Yield, ?Await]
AssignmentExpression[In, Yield, Await]:AsyncArrowFunction[?In, ?Yield, ?Await]

  1. Return true.

AssignmentExpression[In, Yield, Await]:[+Yield]YieldExpression[?In]
AssignmentExpression[In, Yield, Await]:LeftHandSideExpression[?Yield, ?Await]=AssignmentExpression[?In, ?Yield, ?Await]
AssignmentExpression[In, Yield, Await]:LeftHandSideExpression[?Yield, ?Await]AssignmentOperatorAssignmentExpression[?In, ?Yield, ?Await]

  1. Return false.

5.8.2Static Semantics: IsValidSimpleAssignmentTarget#

AssignmentExpression[In, Yield, Await]:[+Yield]YieldExpression[?In]
AssignmentExpression[In, Yield, Await]:ArrowFunction[?In, ?Yield, ?Await]
AssignmentExpression[In, Yield, Await]:AsyncArrowFunction[?In, ?Yield, ?Await]
AssignmentExpression[In, Yield, Await]:LeftHandSideExpression[?Yield, ?Await]=AssignmentExpression[?In, ?Yield, ?Await]
AssignmentExpression[In, Yield, Await]:LeftHandSideExpression[?Yield, ?Await]AssignmentOperatorAssignmentExpression[?In, ?Yield, ?Await]

  1. Return false.

5.9ExpressionStatement#

ExpressionStatement[Yield, Await]:[lookahead ∉ { {, function, class, let [,async[no LineTerminator here]function}]Expression[In, ?Yield, ?Await]

ATodo & Known Issues#

This list is not complete. It also does not duplicate any information on the Github issues list. It serves as a reminder to the author only.

  1. Updates to CreateDynamicFunction for creating dynamic async functions.

BInformative Desugaring#


async function <name>?<argumentlist><body>

=>

function <name>?<argumentlist>{ return spawn(function*() <body>, this); }
  

The spawn used in the above desugaring is a call to the following algorithm. This algorithm does not need to be exposed directly as an API to user code, it is part of the semantics of async functions.


function spawn(genF, self) {
    return new Promise(function(resolve, reject) {
        var gen = genF.call(self);
        function step(nextF) {
            var next;
            try {
                next = nextF();
            } catch(e) {
                // finished with failure, reject the promise
                reject(e);
                return;
            }
            if(next.done) {
                // finished with success, resolve the promise
                resolve(next.value);
                return;
            }
            // not finished, chain off the yielded promise and `step` again
            Promise.resolve(next.value).then(function(v) {
                step(function() { return gen.next(v); });
            }, function(e) {
                step(function() { return gen.throw(e); });
            });
        }
        step(function() { return gen.next(undefined); });
    });
}

CCopyright & Software License#

Copyright Notice

© 2016 Microsoft Inc., Ecma International

Software License

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT http://www.ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.