mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-04 07:40:42 +00:00
Bug 1660538 - Add SMDOC for async function and async generator. r=jorendorff DONTBUILD
Differential Revision: https://phabricator.services.mozilla.com/D87896
This commit is contained in:
parent
5dfb75acf7
commit
d5cbb2ceb8
@ -14,6 +14,266 @@
|
||||
#include "vm/JSObject.h"
|
||||
#include "vm/PromiseObject.h"
|
||||
|
||||
// [SMDOC] Async functions
|
||||
//
|
||||
// # Implementation
|
||||
//
|
||||
// Async functions are implemented based on generators, in terms of
|
||||
// suspend/resume.
|
||||
// Instead of returning the generator object itself, they return the async
|
||||
// function's result promise to the caller.
|
||||
//
|
||||
// The async function's result promise is stored in the generator object
|
||||
// (js::AsyncFunctionGeneratorObject) and retrieved from it whenever the
|
||||
// execution needs it.
|
||||
//
|
||||
//
|
||||
// # Start
|
||||
//
|
||||
// When an async function is called, it synchronously runs until the first
|
||||
// `await` or `return`. This works just like a normal function.
|
||||
//
|
||||
// This corresponds to steps 1-3, 5-9 of AsyncFunctionStart.
|
||||
//
|
||||
// AsyncFunctionStart ( promiseCapability, asyncFunctionBody )
|
||||
// https://tc39.es/ecma262/#sec-async-functions-abstract-operations-async-function-start
|
||||
//
|
||||
// 1. Let runningContext be the running execution context.
|
||||
// 2. Let asyncContext be a copy of runningContext.
|
||||
// 3. NOTE: Copying the execution state is required for the step below to
|
||||
// resume its execution. It is ill-defined to resume a currently executing
|
||||
// context.
|
||||
// ...
|
||||
// 5. Push asyncContext onto the execution context stack; asyncContext is now
|
||||
// the running execution context.
|
||||
// 6. Resume the suspended evaluation of asyncContext. Let result be the value
|
||||
// returned by the resumed computation.
|
||||
// 7. Assert: When we return here, asyncContext has already been removed from
|
||||
// the execution context stack and runningContext is the currently running
|
||||
// execution context.
|
||||
// 8. Assert: result is a normal completion with a value of undefined. The
|
||||
// possible sources of completion values are Await or, if the async
|
||||
// function doesn't await anything, step 4.g above.
|
||||
// 9. Return.
|
||||
//
|
||||
// Unlike generators, async functions don't contain JSOp::InitialYield and
|
||||
// don't suspend immediately when call.
|
||||
//
|
||||
//
|
||||
// # Return
|
||||
//
|
||||
// Explicit/implicit `return` is implemented with the following bytecode
|
||||
// sequence:
|
||||
//
|
||||
// ```
|
||||
// GetAliasedVar ".generator" # VALUE .generator
|
||||
// AsyncResolve 0 # PROMISE
|
||||
// SetRval #
|
||||
// GetAliasedVar ".generator" # .generator
|
||||
// FinalYieldRval #
|
||||
// ```
|
||||
//
|
||||
// JSOp::Resolve (js::AsyncFunctionResolve) resolves the current async
|
||||
// function's result promise. Then this sets it as the function's return value.
|
||||
// (The return value is observable if the caller is still on the stack--
|
||||
// that is, the async function is returning without ever awaiting.
|
||||
// Otherwise we're returning to the microtask loop, which ignores the
|
||||
// return value.)
|
||||
//
|
||||
// This corresponds to AsyncFunctionStart steps 4.a-e. 4.g.
|
||||
//
|
||||
// 4. Set the code evaluation state of asyncContext such that when evaluation
|
||||
// is resumed for that execution context the following steps will be
|
||||
// performed:
|
||||
// a. Let result be the result of evaluating asyncFunctionBody.
|
||||
// b. Assert: If we return here, the async function either threw an
|
||||
// exception or performed an implicit or explicit return; all awaiting
|
||||
// is done.
|
||||
// c. 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.
|
||||
// d. If result.[[Type]] is normal, then
|
||||
// i. Perform
|
||||
// ! Call(promiseCapability.[[Resolve]], undefined, «undefined»).
|
||||
// e. Else if result.[[Type]] is return, then
|
||||
// i. Perform
|
||||
// ! Call(promiseCapability.[[Resolve]], undefined,
|
||||
// «result.[[Value]]»).
|
||||
// ...
|
||||
// g. Return.
|
||||
//
|
||||
//
|
||||
// # Throw
|
||||
//
|
||||
// The body part of an async function is enclosed by an implicit try-catch
|
||||
// block, to catch `throw` completion of the function body.
|
||||
//
|
||||
// If an exception is thrown by the function body, the catch block catches it
|
||||
// and rejects the async function's result promise.
|
||||
//
|
||||
// If there's an expression in parameters, the entire parameters part is also
|
||||
// enclosed by a separate implicit try-catch block.
|
||||
//
|
||||
// ```
|
||||
// Try #
|
||||
// (parameter expressions here) #
|
||||
// Goto BODY #
|
||||
//
|
||||
// JumpTarget from try #
|
||||
// Exception # EXCEPTION
|
||||
// GetAliasedVar ".generator" # EXCEPTION .generator
|
||||
// AsyncResolve 1 # PROMISE
|
||||
// SetRval #
|
||||
// GetAliasedVar ".generator" # .generator
|
||||
// FinalYieldRval #
|
||||
//
|
||||
// BODY:
|
||||
// JumpTarget #
|
||||
// Try #
|
||||
// (body here) #
|
||||
//
|
||||
// JumpTarget from try #
|
||||
// Exception # EXCEPTION
|
||||
// GetAliasedVar ".generator" # EXCEPTION .generator
|
||||
// AsyncResolve 1 # PROMISE
|
||||
// SetRval #
|
||||
// GetAliasedVar ".generator" # .generator
|
||||
// FinalYieldRval #
|
||||
// ```
|
||||
//
|
||||
// This corresponds to AsyncFunctionStart steps 4.f-g.
|
||||
//
|
||||
// 4. ...
|
||||
// f. Else,
|
||||
// i. Assert: result.[[Type]] is throw.
|
||||
// ii. Perform
|
||||
// ! Call(promiseCapability.[[Reject]], undefined,
|
||||
// «result.[[Value]]»).
|
||||
// g. Return.
|
||||
//
|
||||
//
|
||||
// # Await
|
||||
//
|
||||
// `await` is implemented with the following bytecode sequence:
|
||||
// (ignoring TrySkipAwait for now, see "Optimization for await" section)
|
||||
//
|
||||
// ```
|
||||
// (operand here) # VALUE
|
||||
// GetAliasedVar ".generator" # VALUE .generator
|
||||
// AsyncAwait # PROMISE
|
||||
//
|
||||
// GetAliasedVar ".generator" # PROMISE .generator
|
||||
// Await 0 # RVAL GENERATOR RESUMEKIND
|
||||
//
|
||||
// AfterYield # RVAL GENERATOR RESUMEKIND
|
||||
// CheckResumeKind # RVAL
|
||||
// ```
|
||||
//
|
||||
// JSOp::AsyncAwait corresponds to Await steps 1-9, and JSOp::Await corresponds
|
||||
// to Await steps 10-12 in the spec.
|
||||
//
|
||||
// See the next section for JSOp::CheckResumeKind.
|
||||
//
|
||||
// After them, the async function is suspended, and if this is the first await
|
||||
// in the execution, the async function's result promise is returned to the
|
||||
// caller.
|
||||
//
|
||||
// Await
|
||||
// https://tc39.es/ecma262/#await
|
||||
//
|
||||
// 1. Let asyncContext be the running execution context.
|
||||
// 2. Let promise be ? PromiseResolve(%Promise%, value).
|
||||
// 3. Let stepsFulfilled be the algorithm steps defined in Await Fulfilled
|
||||
// Functions.
|
||||
// 4. Let onFulfilled be ! CreateBuiltinFunction(stepsFulfilled, «
|
||||
// [[AsyncContext]] »).
|
||||
// 5. Set onFulfilled.[[AsyncContext]] to asyncContext.
|
||||
// 6. Let stepsRejected be the algorithm steps defined in Await Rejected
|
||||
// Functions.
|
||||
// 7. Let onRejected be ! CreateBuiltinFunction(stepsRejected, «
|
||||
// [[AsyncContext]] »).
|
||||
// 8. Set onRejected.[[AsyncContext]] to asyncContext.
|
||||
// 9. Perform ! PerformPromiseThen(promise, onFulfilled, onRejected).
|
||||
// 10. 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.
|
||||
// 11. Set the code evaluation state of asyncContext such that when evaluation
|
||||
// is resumed with a Completion completion, the following steps of the
|
||||
// algorithm that invoked Await will be performed, with completion
|
||||
// available.
|
||||
// 12. Return.
|
||||
// 13. NOTE: This returns to the evaluation of the operation that had most
|
||||
// previously resumed evaluation of asyncContext.
|
||||
//
|
||||
// (See comments above AsyncAwait and Await in js/src/vm/Opcodes.h for more
|
||||
// details)
|
||||
//
|
||||
//
|
||||
// # Reaction jobs and resume after await
|
||||
//
|
||||
// When an async function performs `await` and the operand becomes settled, a
|
||||
// new reaction job for the operand is enqueued to the job queue.
|
||||
//
|
||||
// The reaction record for the job is marked as "this is for async function"
|
||||
// (see js::AsyncFunctionAwait), and handled specially in
|
||||
// js::PromiseReactionJob.
|
||||
//
|
||||
// When the await operand resolves (either with fulfillment or rejection),
|
||||
// the async function is resumed from the job queue, by calling
|
||||
// js::AsyncFunctionAwaitedFulfilled or js::AsyncFunctionAwaitedRejected
|
||||
// from js::AsyncFunctionPromiseReactionJob.
|
||||
//
|
||||
// The execution resumes from JSOp::AfterYield, with the resolved value
|
||||
// and the resume kind, either normal or throw, corresponds to fulfillment or
|
||||
// rejection, on the stack.
|
||||
//
|
||||
// The resume kind is handled by JSOp::CheckResumeKind after that.
|
||||
//
|
||||
// If the resume kind is normal (=fulfillment), the async function resumes
|
||||
// the execution with the resolved value as the result of `await`.
|
||||
//
|
||||
// If the resume kind is throw (=rejection), it throws the resolved value,
|
||||
// and it will be caught by the try-catch explained above.
|
||||
//
|
||||
//
|
||||
// # Optimization for await
|
||||
//
|
||||
// Suspending the execution and going into the embedding's job queue is slow
|
||||
// and hard to optimize.
|
||||
//
|
||||
// If the following conditions are met, we don't have to perform the above
|
||||
// but just use the await operand as the result of await.
|
||||
//
|
||||
// 1. The await operand is either non-promise or already-fulfilled promise,
|
||||
// so that the result value is already known
|
||||
// 2. There's no jobs in the job queue,
|
||||
// so that we don't have to perform other jobs before resuming from
|
||||
// await
|
||||
// 3. Promise constructor/prototype are not modified,
|
||||
// so that the optimization isn't visible to the user code
|
||||
//
|
||||
// This is implemented by the following bytecode sequence:
|
||||
//
|
||||
// ```
|
||||
// (operand here) # VALUE
|
||||
//
|
||||
// TrySkipAwait # VALUE_OR_RVAL, CAN_SKIP
|
||||
// Not # VALUE_OR_RVAL, !CAN_SKIP
|
||||
// IfEq END # VALUE
|
||||
//
|
||||
// JumpTarget # VALUE
|
||||
// GetAliasedVar ".generator" # VALUE .generator
|
||||
// Await 0 # RVAL GENERATOR RESUMEKIND
|
||||
// AfterYield # RVAL GENERATOR RESUMEKIND
|
||||
// CheckResumeKind # RVAL
|
||||
//
|
||||
// END:
|
||||
// JumpTarget # RVAL
|
||||
// ```
|
||||
//
|
||||
// JSOp::TrySkipAwait checks the above conditions. And if the await can be
|
||||
// skipped, it jumps over the await code.
|
||||
|
||||
namespace js {
|
||||
|
||||
class AsyncFunctionGeneratorObject;
|
||||
|
@ -15,6 +15,270 @@
|
||||
#include "vm/List.h"
|
||||
#include "vm/PromiseObject.h"
|
||||
|
||||
// [SMDOC] Async generators
|
||||
//
|
||||
// # Start
|
||||
//
|
||||
// When an async generator is called, it synchronously runs until the
|
||||
// JSOp::InitialYield and then suspends, just like a sync generator, and returns
|
||||
// an async generator object (js::AsyncGeneratorObject).
|
||||
//
|
||||
//
|
||||
// # Request queue
|
||||
//
|
||||
// When next/return/throw is called on the async generator object,
|
||||
// js::AsyncGeneratorEnqueue performs the following:
|
||||
// * Create a new AsyncGeneratorRequest and enqueue it in the generator
|
||||
// object's request queue.
|
||||
// * Resume the generator with the oldest request, if the generator is
|
||||
// suspended (see "Resume" section below)
|
||||
// * Return the promise for the request
|
||||
//
|
||||
// This is done in js::AsyncGeneratorEnqueue, which corresponds to
|
||||
// AsyncGeneratorEnqueue in the spec,
|
||||
// and js::AsyncGeneratorResumeNext corresponds to the following:
|
||||
// * AsyncGeneratorResolve
|
||||
// * AsyncGeneratorReject
|
||||
// * AsyncGeneratorResumeNext
|
||||
//
|
||||
// The returned promise is resolved when the resumption for the request
|
||||
// completes with yield/throw/return, in js::AsyncGeneratorResolve and
|
||||
// js::AsyncGeneratorReject.
|
||||
// They correspond to AsyncGeneratorResolve and AsyncGeneratorReject in the
|
||||
// spec.
|
||||
//
|
||||
//
|
||||
// # Await
|
||||
//
|
||||
// Async generator's `await` is implemented differently than async function's
|
||||
// `await`.
|
||||
//
|
||||
// The bytecode is the following:
|
||||
// (ignoring TrySkipAwait; see the comment in AsyncFunction.h for more details)
|
||||
//
|
||||
// ```
|
||||
// (operand here) # VALUE
|
||||
// GetAliasedVar ".generator" # VALUE .generator
|
||||
// Await 0 # RVAL GENERATOR RESUMEKIND
|
||||
//
|
||||
// AfterYield # RVAL GENERATOR RESUMEKIND
|
||||
// CheckResumeKind # RVAL
|
||||
// ```
|
||||
//
|
||||
// Async generators don't use JSOp::AsyncAwait, and that part is handled
|
||||
// in js::AsyncGeneratorResume, and js::AsyncGeneratorAwait called there.
|
||||
//
|
||||
// Both JSOp::Await and JSOp::Yield behave in the exactly same way,
|
||||
// and js::AsyncGeneratorResume checks the last opcode and branches for
|
||||
// await/yield/return cases.
|
||||
//
|
||||
//
|
||||
// # Reaction jobs and resume after await
|
||||
//
|
||||
// This is almost same as for async functions (see AsyncFunction.h).
|
||||
//
|
||||
// The reaction record for the job is marked as "this is for async generator"
|
||||
// (see js::AsyncGeneratorAwait), and handled specially in
|
||||
// js::PromiseReactionJob, which calls js::AsyncGeneratorPromiseReactionJob.
|
||||
//
|
||||
//
|
||||
// # Yield
|
||||
//
|
||||
// `yield` is implemented with the following bytecode sequence:
|
||||
// (Ignoring TrySkipAwait for simplicity)
|
||||
//
|
||||
// ```
|
||||
// (operand here) # VALUE
|
||||
// GetAliasedVar ".generator" # VALUE .generator
|
||||
// Await 1 # RVAL GENERATOR RESUMEKIND
|
||||
// AfterYield # RVAL GENERATOR RESUMEKIND
|
||||
// CheckResumeKind # RVAL
|
||||
//
|
||||
// GetAliasedVar ".generator" # RVAL .generator
|
||||
// Yield 2 # RVAL2 GENERATOR RESUMEKIND
|
||||
//
|
||||
// AfterYield # RVAL2 GENERATOR RESUMEKIND
|
||||
// CheckResumeKind # RVAL2
|
||||
// ```
|
||||
//
|
||||
// The 1st part (JSOp::Await + JSOp::CheckResumeKind) performs an implicit
|
||||
// `await`, as specified in AsyncGeneratorYield step 5.
|
||||
//
|
||||
// AsyncGeneratorYield ( value )
|
||||
// https://tc39.es/ecma262/#sec-asyncgeneratoryield
|
||||
//
|
||||
// 5. Set value to ? Await(value).
|
||||
//
|
||||
// The 2nd part (JSOp::Yield) suspends execution and yields the result of
|
||||
// `await`, as specified in AsyncGeneratorYield steps 1-4, 6-7, 9-10.
|
||||
//
|
||||
// AsyncGeneratorYield ( value )
|
||||
// https://tc39.es/ecma262/#sec-asyncgeneratoryield
|
||||
//
|
||||
// 1. Let genContext be the running execution context.
|
||||
// 2. Assert: genContext is the execution context of a generator.
|
||||
// 3. Let generator be the value of the Generator component of genContext.
|
||||
// 4. Assert: GetGeneratorKind() is async.
|
||||
// ..
|
||||
// 6. Set generator.[[AsyncGeneratorState]] to suspendedYield.
|
||||
// 7. 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.
|
||||
// 8. ...
|
||||
// 9. Return ! AsyncGeneratorResolve(generator, value, false).
|
||||
// 10. NOTE: This returns to the evaluation of the operation that had most
|
||||
// previously resumed evaluation of genContext.
|
||||
//
|
||||
// The last part (JSOp::CheckResumeKind) checks the resumption type and
|
||||
// resumes/throws/returns the execution, as specified in AsyncGeneratorYield
|
||||
// step 8.
|
||||
//
|
||||
// 8. Set the code evaluation state of genContext such that when evaluation is
|
||||
// resumed with a Completion resumptionValue the following steps will be
|
||||
// performed:
|
||||
// a. If resumptionValue.[[Type]] is not return, return
|
||||
// Completion(resumptionValue).
|
||||
// b. Let awaited be Await(resumptionValue.[[Value]]).
|
||||
// c. If awaited.[[Type]] is throw, return Completion(awaited).
|
||||
// d. Assert: awaited.[[Type]] is normal.
|
||||
// e. Return Completion { [[Type]]: return, [[Value]]: awaited.[[Value]],
|
||||
// [[Target]]: empty }.
|
||||
// f. NOTE: When one of the above steps returns, it returns to the
|
||||
// evaluation of the YieldExpression production that originally called
|
||||
// this abstract operation.
|
||||
//
|
||||
// Resumption with `AsyncGenerator.prototype.return` is handled differently.
|
||||
// See "Resumption with return" section below.
|
||||
//
|
||||
//
|
||||
// # Return
|
||||
//
|
||||
// `return` with operand is implemented with the following bytecode sequence:
|
||||
// (Ignoring TrySkipAwait for simplicity)
|
||||
//
|
||||
// ```
|
||||
// (operand here) # VALUE
|
||||
// GetAliasedVar ".generator" # VALUE .generator
|
||||
// Await 0 # RVAL GENERATOR RESUMEKIND
|
||||
// AfterYield # RVAL GENERATOR RESUMEKIND
|
||||
// CheckResumeKind # RVAL
|
||||
//
|
||||
// SetRval #
|
||||
// GetAliasedVar ".generator" # .generator
|
||||
// FinalYieldRval #
|
||||
// ```
|
||||
//
|
||||
// The 1st part (JSOp::Await + JSOp::CheckResumeKind) performs implicit
|
||||
// `await`, as specified in ReturnStatement's Evaluation step 3.
|
||||
//
|
||||
// ReturnStatement: return Expression;
|
||||
// https://tc39.es/ecma262/#sec-return-statement-runtime-semantics-evaluation
|
||||
//
|
||||
// 3. If ! GetGeneratorKind() is async, set exprValue to ? Await(exprValue).
|
||||
//
|
||||
// And the 2nd part corresponds to AsyncGeneratorStart steps 5.a-e, 5.g.
|
||||
//
|
||||
// AsyncGeneratorStart ( generator, generatorBody )
|
||||
// https://tc39.es/ecma262/#sec-asyncgeneratorstart
|
||||
//
|
||||
// 5. Set the code evaluation state of genContext such that when evaluation
|
||||
// is resumed for that execution context the following steps will be
|
||||
// performed:
|
||||
// a. Let result be the result of evaluating generatorBody.
|
||||
// b. Assert: If we return here, the async generator either threw an
|
||||
// exception or performed either an implicit or explicit return.
|
||||
// c. 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.
|
||||
// d. Set generator.[[AsyncGeneratorState]] to completed.
|
||||
// e. If result is a normal completion, let resultValue be undefined.
|
||||
// ...
|
||||
// g. Return ! AsyncGeneratorResolve(generator, resultValue, true).
|
||||
//
|
||||
// `return` without operand or implicit return is implicit with the following
|
||||
// bytecode sequence:
|
||||
//
|
||||
// ```
|
||||
// Undefined # undefined
|
||||
// SetRval #
|
||||
// GetAliasedVar ".generator" # .generator
|
||||
// FinalYieldRval #
|
||||
// ```
|
||||
//
|
||||
// This is also AsyncGeneratorStart steps 5.a-e, 5.g.
|
||||
//
|
||||
//
|
||||
// # Throw
|
||||
//
|
||||
// Unlike async function, async generator doesn't use implicit try-catch,
|
||||
// but the throw completion is handled by js::AsyncGeneratorResume,
|
||||
// and js::AsyncGeneratorThrown is called there.
|
||||
//
|
||||
// 5. ...
|
||||
// f. Else,
|
||||
// i. Let resultValue be result.[[Value]].
|
||||
// ii. If result.[[Type]] is not return, then
|
||||
// 1. Return ! AsyncGeneratorReject(generator, resultValue).
|
||||
//
|
||||
//
|
||||
// # Resumption with return
|
||||
//
|
||||
// Resumption with return completion is handled in js::AsyncGeneratorResumeNext.
|
||||
//
|
||||
// If the generator is suspended, it doesn't immediately resume the generator
|
||||
// script itself, but handles implicit `await` it in
|
||||
// js::AsyncGeneratorResumeNext.
|
||||
// (See PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled and
|
||||
// PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected), and resumes the
|
||||
// generator with the result of await.
|
||||
// And the return completion is finally handled in JSOp::CheckResumeKind
|
||||
// after JSOp::Yield.
|
||||
//
|
||||
// This corresponds to AsyncGeneratorYield step 8.
|
||||
//
|
||||
// AsyncGeneratorYield ( value )
|
||||
// https://tc39.es/ecma262/#sec-asyncgeneratoryield
|
||||
//
|
||||
// 8. Set the code evaluation state of genContext such that when evaluation
|
||||
// is resumed with a Completion resumptionValue the following steps will
|
||||
// be performed:
|
||||
// ..
|
||||
// b. Let awaited be Await(resumptionValue.[[Value]]).
|
||||
// c. If awaited.[[Type]] is throw, return Completion(awaited).
|
||||
// d. Assert: awaited.[[Type]] is normal.
|
||||
// e. Return Completion { [[Type]]: return, [[Value]]: awaited.[[Value]],
|
||||
// [[Target]]: empty }.
|
||||
//
|
||||
// If the generator is already completed, it awaits on the return value,
|
||||
// (See PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled and
|
||||
// PromiseHandlerAsyncGeneratorResumeNextReturnRejected), and resolves the
|
||||
// request's promise with the value.
|
||||
//
|
||||
// It corresponds to AsyncGeneratorResumeNext step 10.b.i.
|
||||
//
|
||||
// AsyncGeneratorResumeNext ( generator )
|
||||
// https://tc39.es/ecma262/#sec-asyncgeneratorresumenext
|
||||
//
|
||||
// 10. If completion is an abrupt completion, then
|
||||
// ..
|
||||
// b. If state is completed, then
|
||||
// i. If completion.[[Type]] is return, then
|
||||
// 1. Set generator.[[AsyncGeneratorState]] to awaiting-return.
|
||||
// 2. Let promise be ? PromiseResolve(%Promise%, completion.[[Value]]).
|
||||
// 3. Let stepsFulfilled be the algorithm steps defined in
|
||||
// AsyncGeneratorResumeNext Return Processor Fulfilled Functions.
|
||||
// 4. Let onFulfilled be ! CreateBuiltinFunction(stepsFulfilled, «
|
||||
// [[Generator]] »).
|
||||
// 5. Set onFulfilled.[[Generator]] to generator.
|
||||
// 6. Let stepsRejected be the algorithm steps defined in
|
||||
// AsyncGeneratorResumeNext Return Processor Rejected Functions.
|
||||
// 7. Let onRejected be ! CreateBuiltinFunction(stepsRejected, «
|
||||
// [[Generator]] »).
|
||||
// 8. Set onRejected.[[Generator]] to generator.
|
||||
// 9. Perform ! PerformPromiseThen(promise, onFulfilled, onRejected).
|
||||
// 10. Return undefined.
|
||||
//
|
||||
|
||||
namespace js {
|
||||
|
||||
class AsyncGeneratorObject;
|
||||
|
Loading…
x
Reference in New Issue
Block a user