Stage 3 (Conditional on Editor Review) Draft / December 27, 2023

ES Array.fromAsync (2022)

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.

This proposal depends on the pull request tc39/ecma262#2942: β€œsupport built-in async functions”.

1 Control Abstraction Objects

1.1 Iteration

1.1.1 Iterator Abstract Operations

1.1.1.1 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).
    2. Return value.
  2. Else if value is a Completion Record, set value to value.[[Value]].

2 Indexed Collections

2.1 Array Objects

2.1.1 Properties of the Array Constructor

2.1.1.1 Array.fromAsync ( asyncItems [ , mapfn [ , thisArg ] ] )

Editor's Note

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.
  2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  3. 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.
    2. Else,
      1. If IsCallable(mapfn) is false, throw a TypeError exception.
      2. Let mapping be true.
    3. Let usingAsyncIterator be ? GetMethod(asyncItems, @@asyncIterator).
    4. If usingAsyncIterator is undefined, then
      1. Let usingSyncIterator be ? GetMethod(asyncItems, @@iterator).
    5. Let iteratorRecord be undefined.
    6. If usingAsyncIterator is not undefined, then
      1. Set iteratorRecord to ? GetIterator(asyncItems, async, usingAsyncIterator).
    7. Else if usingSyncIterator is not undefined, then
      1. Set iteratorRecord to ? CreateAsyncFromSyncIterator(GetIterator(asyncItems, sync, usingSyncIterator)).
    8. If iteratorRecord is not undefined, then
      1. If IsConstructor(C) is true, then
        1. Let A be ? Construct(C).
      2. Else,
        1. Let A be ! ArrayCreate(0).
      3. Let k be 0.
      4. Repeat,
        1. If k β‰₯ 253 - 1, then
          1. Let error be ThrowCompletion(a newly created TypeError object).
          2. Return ? AsyncIteratorClose(iteratorRecord, error).
        2. Let Pk be ! ToString(𝔽(k)).
        3. Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]).
        4. Set nextResult to ? Await(nextResult).
        5. If nextResult is not an Object, throw a TypeError exception.
        6. Let done be ? IteratorComplete(nextResult).
        7. If done is true,
          1. Perform ? Set(A, "length", 𝔽(k), true).
          2. Return Completion Record { [[Type]]: return, [[Value]]: A, [[Target]]: empty }.
        8. Let nextValue be ? IteratorValue(nextResult).
        9. If mapping is true, then
          1. Let mappedValue be Call(mapfn, thisArg, Β« nextValue, 𝔽(k) Β»).
          2. IfAbruptCloseAsyncIterator(mappedValue, iteratorRecord).
          3. Set mappedValue to Await(mappedValue).
          4. IfAbruptCloseAsyncIterator(mappedValue, iteratorRecord).
        10. Else, let mappedValue be nextValue.
        11. Let defineStatus be CreateDataPropertyOrThrow(A, Pk, mappedValue).
        12. If defineStatus is an abrupt completion, return ? AsyncIteratorClose(iteratorRecord, defineStatus).
        13. Set k to k + 1.
    9. Else,
      1. NOTE: asyncItems is neither an AsyncIterable nor an Iterable so assume it is an array-like object.
      2. Let arrayLike be ! ToObject(asyncItems).
      3. Let len be ? LengthOfArrayLike(arrayLike).
      4. If IsConstructor(C) is true, then
        1. Let A be ? Construct(C, Β« 𝔽(len) Β»).
      5. Else,
        1. Let A be ? ArrayCreate(len).
      6. Let k be 0.
      7. Repeat, while k < len,
        1. Let Pk be ! ToString(𝔽(k)).
        2. Let kValue be ? Get(arrayLike, Pk).
        3. Set kValue to ? Await(kValue).
        4. If mapping is true, then
          1. Let mappedValue be ? Call(mapfn, thisArg, Β« kValue, 𝔽(k) Β»).
          2. Set mappedValue to ? Await(mappedValue).
        5. Else, let mappedValue be kValue.
        6. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).
        7. Set k to k + 1.
      8. Perform ? Set(A, "length", 𝔽(len), true).
      9. Return Completion Record { [[Type]]: return, [[Value]]: A, [[Target]]: empty }.
  4. Perform AsyncFunctionStart(promiseCapability, fromAsyncClosure).
  5. Return promiseCapability.[[Promise]].
Note

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.