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.

Control Abstraction Objects

Iteration

Iterator Abstract Operations

IfAbruptCloseAsyncIterator ( _value_, _iteratorRecord_ )

IfAbruptCloseAsyncIterator is a shorthand for a sequence of algorithm steps that use an Iterator Record. An algorithm step of the form:

1. IfAbruptCloseAsyncIterator(_value_, _iteratorRecord_).

means the same thing as:

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.

Indexed Collections

Array Objects

Properties of the Array Constructor

Array.fromAsync ( _asyncItems_ [ , _mapfn_ [ , _thisArg_ ] ] )

This section is a wholly new subsection of the original Properties of the Array Constructor clause, to be inserted before the Array.from clause.

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.