title: ES Array.fromAsync (2022)
status: proposal
stage: 1
location: https://github.com/js-choi/proposal-array-async-from
copyright: false
contributors: J. S. Choi
Introduction
This is the formal specification for a proposed `Array.fromAsync` factory method
in JavaScript. It modifies the original ECMAScript specification with
several new or revised clauses. See the proposal's
explainer for the proposal's background, motivation, and usage examples.
1. If _value_ is an abrupt completion, then
1. Perform ? AsyncIteratorClose(_iteratorRecord_, _value_).
1. Return _value_.
1. Else if _value_ is a Completion Record, set _value_ to _value_.[[Value]].
AsyncFunction Objects
Async Functions Abstract Operations
AsyncBlockStart (
_promiseCapability_: a PromiseCapability Record,
_asyncBody_: a Parse Node or an Abstract Closure with no parameters,
_asyncContext_: an execution context,
)
1. Assert: _promiseCapability_ is a PromiseCapability Record.
1. Let _runningContext_ be the running execution context.
1. [fence-effects="user-code"] Set the code evaluation state of _asyncContext_ such that when evaluation is resumed for that execution context the following steps will be performed:
1. If _asyncBody_ is a Parse Node, then
1. Let _result_ be the result of evaluating _asyncBody_.
1. Else,
1. Assert: _asyncBody_ is an Abstract Closure with no parameters.
1. Let _result_ be _asyncBody_().
1. Let _result_ be the result of evaluating _asyncBody_.
1. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done.
1. 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.
1. If _result_.[[Type]] is ~normal~, then
1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « *undefined* »).
1. Else if _result_.[[Type]] is ~return~, then
1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _result_.[[Value]] »).
1. Else,
1. Assert: _result_.[[Type]] is ~throw~.
1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « _result_.[[Value]] »).
1. [id="step-asyncblockstart-return-undefined"] Return.
1. Push _asyncContext_ onto the execution context stack; _asyncContext_ is now the running execution context.
1. Resume the suspended evaluation of _asyncContext_. Let _result_ be the value returned by the resumed computation.
1. Assert: When we return here, _asyncContext_ has already been removed from the execution context stack and _runningContext_ is the currently running execution context.
1. 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 above.
1. Return.
When the `fromAsync` method is called, the following steps are taken:
1. Let _C_ be the *this* value.
1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%).
1. Let _fromAsyncClosure_ be a new Abstract Closure with no parameters that captures _C_, _mapfn_, and _thisArg_ and performs the following steps when called:
1. If _mapfn_ is *undefined*, let _mapping_ be *false*.
1. Else,
1. If IsCallable(_mapfn_) is *false*, throw a *TypeError* exception.
1. Let _mapping_ be *true*.
1. Let _usingAsyncIterator_ be ? GetMethod(_asyncItems_, @@asyncIterator).
1. If _usingAsyncIterator_ is *undefined*, then
1. Let _usingSyncIterator_ be ? GetMethod(_asyncItems_, @@iterator).
1. If IsConstructor(_C_) is *true*, then
1. Let _A_ be ? Construct(_C_).
1. Else,
1. Let _A_ be ! ArrayCreate(0).
1. Let _iteratorRecord_ be *undefined*.
1. If _usingAsyncIterator_ is not *undefined*, then
1. Set _iteratorRecord_ to ? GetIterator(_asyncItems_, ~async~, _usingAsyncIterator_).
1. Else if _usingSyncIterator_ is not *undefined*, then
1. Set _iteratorRecord_ to ? CreateAsyncFromSyncIterator(GetIterator(_asyncItems_, ~sync~, _usingSyncIterator_)).
1. If _iteratorRecord_ is not *undefined*, then
1. Let _k_ be 0.
1. Repeat,
1. If _k_ ≥ 253 - 1, then
1. Let _error_ be ThrowCompletion(a newly created *TypeError* object).
1. Return ? AsyncIteratorClose(_iteratorRecord_, _error_).
1. Let _Pk_ be ! ToString(𝔽(_k_)).
1. Let _next_ be ? Await(IteratorStep(_iteratorRecord_)).
1. If _next_ is *false*, then
1. Perform ? Set(_A_, *"length"*, 𝔽(_k_), *true*).
1. Return Completion Record { [[Type]]: ~return~, [[Value]]: _A_, [[Target]]: ~empty~ }.
1. Let _nextValue_ be ? IteratorValue(_next_).
1. If _mapping_ is *true*, then
1. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »).
1. IfAbruptCloseAsyncIterator(_mappedValue_, _iteratorRecord_).
1. Set _mappedValue_ to Await(_mappedValue_).
1. IfAbruptCloseAsyncIterator(_mappedValue_, _iteratorRecord_).
1. Else, let _mappedValue_ be _nextValue_.
1. Let _defineStatus_ be CreateDataPropertyOrThrow(_A_, _Pk_, _mappedValue_).
1. If _defineStatus_ is an abrupt completion, return ? AsyncIteratorClose(_iteratorRecord_, _defineStatus_).
1. Set _k_ to _k_ + 1.
1. Else,
1. NOTE: _asyncItems_ is neither an AsyncIterable nor an Iterable so assume it is an array-like object.
1. Let _arrayLike_ be ! ToObject(_asyncItems_).
1. Let _len_ be ? LengthOfArrayLike(_arrayLike_).
1. If IsConstructor(_C_) is *true*, then
1. Let _A_ be ? Construct(_C_, « 𝔽(_len_) »).
1. Else,
1. Let _A_ be ? ArrayCreate(_len_).
1. Let _k_ be 0.
1. Repeat, while _k_ < _len_,
1. Let _Pk_ be ! ToString(𝔽(_k_)).
1. Let _kValue_ be ? Get(_arrayLike_, _Pk_).
1. Let _kValue_ be ? Await(_kValue_).
1. If _mapping_ is *true*, then
1. Let _mappedValue_ be ? Call(_mapfn_, _thisArg_, « _kValue_, 𝔽(_k_) »).
1. Let _mappedValue_ be ? Await(_mappedValue_).
1. Else, let _mappedValue_ be _kValue_.
1. Perform ? CreateDataPropertyOrThrow(_A_, _Pk_, _mappedValue_).
1. Set _k_ to _k_ + 1.
1. Perform ? Set(_A_, *"length"*, 𝔽(_len_), *true*).
1. Return Completion Record { [[Type]]: ~return~, [[Value]]: _A_, [[Target]]: ~empty~ }.
1. Perform AsyncFunctionStart(_promiseCapability_, _fromAsyncClosure_).
1. Return _promiseCapability_.[[Promise]].
The `fromAsync` function is an intentionally generic factory method; it does not require that its *this* value be the Array constructor. Therefore it can be transferred to or inherited by any other constructors that may be called with a single numeric argument.