Stage 2 Draft / April 10, 2024

Async Iterator Helpers

Contributing to this Proposal

This proposal is developed on GitHub with the help of the ECMAScript community. There are a number of ways to contribute to the development of this specification:

1 Well-Known Intrinsic Objects

Table 1: Well-Known Intrinsic Objects
Intrinsic Name Global Name ECMAScript Language Association
%AsyncIterator% AsyncIterator The AsyncIterator constructor (2.1.2.1)
%AsyncIteratorPrototype% AsyncIterator.prototype An object that all standard built-in async iterator objects indirectly inherit from

The initial value of the "prototype" data property of %AsyncIterator%; i.e., %AsyncIterator.prototype%

2 Control Abstraction Objects

2.1 Iteration

2.1.1 Iterator Abstract Operations

2.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.1.1.2 AwaitNonPrimitive ( value )

The abstract operation AwaitNonPrimitive takes argument value (an ECMAScript language value) and returns either a normal completion containing an ECMAScript language value or a throw completion. It performs the following steps when called:

  1. If value is an Object, return ? Await(value).
  2. Else, return value.

2.1.2 AsyncIterator Objects

2.1.2.1 The AsyncIterator Constructor

The AsyncIterator constructor:

  • is %AsyncIterator%.
  • is the initial value of the "AsyncIterator" property of the global object.
  • is designed to be subclassable. It may be used as the value of an extends clause of a class definition.

2.1.2.1.1 AsyncIterator ( )

When the AsyncIterator function is called, the following steps are taken:

  1. If NewTarget is undefined or the active function object, throw a TypeError exception.
  2. Return ? OrdinaryCreateFromConstructor(NewTarget, "%AsyncIterator.prototype%").

2.1.2.2 Properties of the AsyncIterator Constructor

2.1.2.2.1 AsyncIterator.prototype

The initial value of AsyncIterator.prototype is %AsyncIterator.prototype%.

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

2.1.2.2.2 AsyncIterator.from ( O )

  1. If O is a String, set O to ! ToObject(O).
  2. Let iteratorRecord be ? GetIteratorFlattenable(O, async).
  3. Let hasInstance be ? OrdinaryHasInstance(%AsyncIterator%, iteratorRecord.[[Iterator]]).
  4. If hasInstance is true, then
    1. Return iteratorRecord.[[Iterator]].
  5. Let wrapper be OrdinaryObjectCreate(%WrapForValidAsyncIteratorPrototype%, « [[AsyncIterated]] »).
  6. Set wrapper.[[AsyncIterated]] to iteratorRecord.
  7. Return wrapper.

2.1.2.2.2.1 The %WrapForValidAsyncIteratorPrototype% Object

The %WrapForValidAsyncIteratorPrototype% object:

2.1.2.2.2.1.1 %WrapForValidAsyncIteratorPrototype%.next ( )

  1. Let O be this value.
  2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  3. Let check be Completion(RequireInternalSlot(O, [[AsyncIterated]])).
  4. IfAbruptRejectPromise(check, promiseCapability).
  5. Let result be Completion(IteratorNext(O.[[AsyncIterated]])).
  6. IfAbruptRejectPromise(result, promiseCapability).
  7. Return ? PromiseResolve(%Promise%, result).

2.1.2.2.2.1.2 %WrapForValidAsyncIteratorPrototype%.return ( )

  1. Let O be this value.
  2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  3. Let check be Completion(RequireInternalSlot(O, [[AsyncIterated]])).
  4. IfAbruptRejectPromise(check, promiseCapability).
  5. Let iterator be O.[[AsyncIterated]].[[Iterator]].
  6. Assert: iterator is an Object.
  7. Let returnMethod be Completion(GetMethod(iterator, "return")).
  8. IfAbruptRejectPromise(returnMethod, promiseCapability).
  9. If returnMethod is undefined, then
    1. Return ! PromiseResolve(%Promise%, CreateIterResultObject(undefined, true)).
  10. Let result be Completion(Call(returnMethod, iterator)).
  11. IfAbruptRejectPromise(result, promiseCapability).
  12. Return ? PromiseResolve(%Promise%, result).

2.1.3 Async Iterator Helper Objects

An Async Iterator Helper object is an ordinary object that represents a lazy transformation of some specific source async iterator object. There is not a named constructor for Async Iterator Helper objects. Instead, Async Iterator Helper objects are created by calling certain methods of AsyncIterator instance objects.

2.1.3.1 The %AsyncIteratorHelperPrototype% Object

The %AsyncIteratorHelperPrototype% object:

2.1.3.1.1 %AsyncIteratorHelperPrototype%.next ( )

  1. Return AsyncGeneratorNext(this value, "Async Iterator Helper", undefined).

2.1.3.1.2 %AsyncIteratorHelperPrototype%.return ( )

  1. Return AsyncGeneratorReturn(this value, "Async Iterator Helper", undefined).

2.1.3.1.3 %AsyncIteratorHelperPrototype% [ @@toStringTag ]

The initial value of the @@toStringTag property is the String value "Async Iterator Helper".

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

2.1.4 Iterator.prototype

2.1.4.1 Iterator.prototype.toAsync ( )

This method performs the following steps when called:

  1. Let syncIteratorRecord be ? GetIteratorDirect(this value).
  2. Let asyncIteratorRecord be CreateAsyncFromSyncIterator(syncIteratorRecord).
  3. Let wrapper be OrdinaryObjectCreate(%WrapForValidAsyncIteratorPrototype%, « [[AsyncIterated]] »).
  4. Set wrapper.[[AsyncIterated]] to asyncIteratorRecord.
  5. Return wrapper.

2.1.5 AsyncIterator.prototype

The AsyncIterator prototype object:

2.1.5.1 AsyncIterator.prototype.constructor

The initial value of AsyncIterator.prototype.constructor is %AsyncIterator%.

2.1.5.2 AsyncIterator.prototype.map ( mapper )

This method performs the following steps when called:

  1. Let iterated be ? GetIteratorDirect(this value).
  2. If IsCallable(mapper) is false, throw a TypeError exception.
  3. Let closure be a new Abstract Closure with no parameters that captures iterated and mapper and performs the following steps when called:
    1. Let counter be 0.
    2. Repeat,
      1. Let next be ? Await(? IteratorNext(iterated)).
      2. If ? IteratorComplete(next) is true, return undefined.
      3. Let value be ? IteratorValue(next).
      4. Let mapped be Completion(Call(mapper, undefined, « value, 𝔽(counter) »)).
      5. IfAbruptCloseAsyncIterator(mapped, iterated).
      6. Set mapped to Completion(AwaitNonPrimitive(mapped)).
      7. IfAbruptCloseAsyncIterator(mapped, iterated).
      8. Let completion be Completion(Yield(mapped)).
      9. IfAbruptCloseAsyncIterator(completion, iterated).
      10. Set counter to counter + 1.
  4. Return CreateAsyncIteratorFromClosure(closure, "Async Iterator Helper", %AsyncIteratorHelperPrototype%).

2.1.5.3 AsyncIterator.prototype.filter ( predicate )

This method performs the following steps when called:

  1. Let iterated be ? GetIteratorDirect(this value).
  2. If IsCallable(predicate) is false, throw a TypeError exception.
  3. Let closure be a new Abstract Closure with no parameters that captures iterated and predicate and performs the following steps when called:
    1. Let counter be 0.
    2. Repeat,
      1. Let next be ? Await(? IteratorNext(iterated)).
      2. If ? IteratorComplete(next) is true, return undefined.
      3. Let value be ? IteratorValue(next).
      4. Let selected be Completion(Call(predicate, undefined, « value, 𝔽(counter) »)).
      5. IfAbruptCloseAsyncIterator(selected, iterated).
      6. Set selected to Completion(AwaitNonPrimitive(selected)).
      7. IfAbruptCloseAsyncIterator(selected, iterated).
      8. If ToBoolean(selected) is true, then
        1. Let completion be Completion(Yield(value)).
        2. IfAbruptCloseAsyncIterator(completion, iterated).
      9. Set counter to counter + 1.
  4. Return CreateAsyncIteratorFromClosure(closure, "Async Iterator Helper", %AsyncIteratorHelperPrototype%).

2.1.5.4 AsyncIterator.prototype.take ( limit )

This method performs the following steps when called:

  1. Let iterated be ? GetIteratorDirect(this value).
  2. Let numLimit be ? ToNumber(limit).
  3. If numLimit is NaN, throw a RangeError exception.
  4. Let integerLimit be ! ToIntegerOrInfinity(numLimit).
  5. If integerLimit < 0, throw a RangeError exception.
  6. Let closure be a new Abstract Closure with no parameters that captures iterated and integerLimit and performs the following steps when called:
    1. Let remaining be integerLimit.
    2. Repeat,
      1. If remaining is 0, then
        1. Return ? AsyncIteratorClose(iterated, NormalCompletion(undefined)).
      2. If remaining is not +∞, then
        1. Set remaining to remaining - 1.
      3. Let next be ? Await(? IteratorNext(iterated)).
      4. If ? IteratorComplete(next) is true, return undefined.
      5. Let completion be Completion(Yield(? IteratorValue(next))).
      6. IfAbruptCloseAsyncIterator(completion, iterated).
  7. Return CreateAsyncIteratorFromClosure(closure, "Async Iterator Helper", %AsyncIteratorHelperPrototype%).

2.1.5.5 AsyncIterator.prototype.drop ( limit )

This method performs the following steps when called:

  1. Let iterated be ? GetIteratorDirect(this value).
  2. Let numLimit be ? ToNumber(limit).
  3. If numLimit is NaN, throw a RangeError exception.
  4. Let integerLimit be ! ToIntegerOrInfinity(numLimit).
  5. If integerLimit < 0, throw a RangeError exception.
  6. Let closure be a new Abstract Closure with no parameters that captures iterated and integerLimit and performs the following steps when called:
    1. Let remaining be integerLimit.
    2. Repeat, while remaining > 0,
      1. If remaining is not +∞, then
        1. Set remaining to remaining - 1.
      2. Let next be ? Await(? IteratorNext(iterated)).
      3. If ? IteratorComplete(next) is true, return undefined.
    3. Repeat,
      1. Let next be ? Await(? IteratorNext(iterated)).
      2. If ? IteratorComplete(next) is true, return undefined.
      3. Let completion be Completion(Yield(? IteratorValue(next))).
      4. IfAbruptCloseAsyncIterator(completion, iterated).
  7. Return CreateAsyncIteratorFromClosure(closure, "Async Iterator Helper", %AsyncIteratorHelperPrototype%).

2.1.5.6 AsyncIterator.prototype.flatMap ( mapper )

This method performs the following steps when called:

  1. Let iterated be ? GetIteratorDirect(this value).
  2. If IsCallable(mapper) is false, throw a TypeError exception.
  3. Let closure be a new Abstract Closure with no parameters that captures iterated and mapper and performs the following steps when called:
    1. Let counter be 0.
    2. Repeat,
      1. Let next be ? Await(? IteratorNext(iterated)).
      2. If ? IteratorComplete(next) is true, return undefined.
      3. Let value be ? IteratorValue(next).
      4. Let mapped be Completion(Call(mapper, undefined, « value, 𝔽(counter) »)).
      5. IfAbruptCloseAsyncIterator(mapped, iterated).
      6. Set mapped to Completion(AwaitNonPrimitive(mapped)).
      7. IfAbruptCloseAsyncIterator(mapped, iterated).
      8. Let innerIterator be Completion(GetIteratorFlattenable(mapped, async)).
      9. IfAbruptCloseAsyncIterator(innerIterator, iterated).
      10. Let innerAlive be true.
      11. Repeat, while innerAlive is true,
        1. Let innerNextPromise be Completion(IteratorNext(innerIterator)).
        2. IfAbruptCloseAsyncIterator(innerNextPromise, iterated).
        3. Let innerNext be Completion(Await(innerNextPromise)).
        4. IfAbruptCloseAsyncIterator(innerNext, iterated).
        5. Let innerComplete be Completion(IteratorComplete(innerNext)).
        6. IfAbruptCloseAsyncIterator(innerComplete, iterated).
        7. If innerComplete is true, then
          1. Set innerAlive to false.
        8. Else,
          1. Let innerValue be Completion(IteratorValue(innerNext)).
          2. IfAbruptCloseAsyncIterator(innerValue, iterated).
          3. Let completion be Completion(Yield(innerValue)).
          4. If completion is a return completion, then
            1. Let backupCompletion be Completion(IteratorClose(innerIterator, completion)).
            2. IfAbruptCloseIterator(backupCompletion, iterated).
            3. Return ? IteratorClose(completion, iterated).
          5. Else if completion is a throw completion, then
            1. Assert: Awaiting innerValue during the Yield on step 3.b.xi.8.c threw.
            2. Return ? IteratorClose(completion, iterated).
      12. Set counter to counter + 1.
  4. Return CreateAsyncIteratorFromClosure(closure, "Async Iterator Helper", %AsyncIteratorHelperPrototype%).

2.1.5.7 AsyncIterator.prototype.reduce ( reducer [ , initialValue ] )

This async method performs the following steps when called:

  1. Let iterated be ? GetIteratorDirect(this value).
  2. If IsCallable(reducer) is false, throw a TypeError exception.
  3. If initialValue is not present, then
    1. Let next be ? Await(? IteratorNext(iterated)).
    2. If ? IteratorComplete(next) is true, throw a TypeError exception.
    3. Let accumulator be ? IteratorValue(next).
    4. Let counter be 1.
  4. Else,
    1. Let accumulator be initialValue.
    2. Let counter be 0.
  5. Repeat,
    1. Let next be ? Await(? IteratorNext(iterated)).
    2. If ? IteratorComplete(next) is true, return accumulator.
    3. Let value be ? IteratorValue(next).
    4. Let result be Completion(Call(reducer, undefined, « accumulator, value, 𝔽(counter) »)).
    5. IfAbruptCloseAsyncIterator(result, iterated).
    6. Set result to Completion(AwaitNonPrimitive(result)).
    7. IfAbruptCloseAsyncIterator(result, iterated).
    8. Set accumulator to result.
    9. Set counter to counter + 1.

2.1.5.8 AsyncIterator.prototype.toArray ( )

This async method performs the following steps when called:

  1. Let iterated be ? GetIteratorDirect(this value).
  2. Let items be a new empty List.
  3. Repeat,
    1. Let next be ? Await(? IteratorNext(iterated)).
    2. If ? IteratorComplete(next) is true, return CreateArrayFromList(items).
    3. Let value be ? IteratorValue(next).
    4. Append value to items.

2.1.5.9 AsyncIterator.prototype.forEach ( fn )

This async method performs the following steps when called:

  1. Let iterated be ? GetIteratorDirect(this value).
  2. If IsCallable(fn) is false, throw a TypeError exception.
  3. Let counter be 0.
  4. Repeat,
    1. Let next be ? Await(? IteratorNext(iterated)).
    2. If ? IteratorComplete(next) is true, return undefined.
    3. Let value be ? IteratorValue(next).
    4. Let r be Completion(Call(fn, undefined, « value, 𝔽(counter) »)).
    5. IfAbruptCloseAsyncIterator(r, iterated).
    6. Set r to Completion(AwaitNonPrimitive(r)).
    7. IfAbruptCloseAsyncIterator(r, iterated).
    8. Set counter to counter + 1.

2.1.5.10 AsyncIterator.prototype.some ( predicate )

This async method performs the following steps when called:

  1. Let iterated be ? GetIteratorDirect(this value).
  2. If IsCallable(predicate) is false, throw a TypeError exception.
  3. Let counter be 0.
  4. Repeat,
    1. Let next be ? Await(? IteratorNext(iterated)).
    2. If ? IteratorComplete(next) is true, return false.
    3. Let value be ? IteratorValue(next).
    4. Let result be Completion(Call(predicate, undefined, « value, 𝔽(counter) »)).
    5. IfAbruptCloseAsyncIterator(result, iterated).
    6. Set result to Completion(AwaitNonPrimitive(result)).
    7. IfAbruptCloseAsyncIterator(result, iterated).
    8. If ToBoolean(result) is true, return ? AsyncIteratorClose(iterated, NormalCompletion(true)).
    9. Set counter to counter + 1.

2.1.5.11 AsyncIterator.prototype.every ( predicate )

This async method performs the following steps when called:

  1. Let iterated be ? GetIteratorDirect(this value).
  2. If IsCallable(predicate) is false, throw a TypeError exception.
  3. Let counter be 0.
  4. Repeat,
    1. Let next be ? Await(? IteratorNext(iterated)).
    2. If ? IteratorComplete(next) is true, return true.
    3. Let value be ? IteratorValue(next).
    4. Let result be Completion(Call(predicate, undefined, « value, 𝔽(counter) »)).
    5. IfAbruptCloseAsyncIterator(result, iterated).
    6. Set result to Completion(AwaitNonPrimitive(result)).
    7. IfAbruptCloseAsyncIterator(result, iterated).
    8. If ToBoolean(result) is false, return ? AsyncIteratorClose(iterated, NormalCompletion(false)).
    9. Set counter to counter + 1.

2.1.5.12 AsyncIterator.prototype.find ( predicate )

This async method performs the following steps when called:

  1. Let iterated be ? GetIteratorDirect(this value).
  2. If IsCallable(predicate) is false, throw a TypeError exception.
  3. Let counter be 0.
  4. Repeat,
    1. Let next be ? Await(? IteratorNext(iterated)).
    2. If ? IteratorComplete(next) is true, return undefined.
    3. Let value be ? IteratorValue(next).
    4. Let result be Completion(Call(predicate, undefined, « value, 𝔽(counter) »)).
    5. IfAbruptCloseAsyncIterator(result, iterated).
    6. Set result to Completion(AwaitNonPrimitive(result)).
    7. IfAbruptCloseAsyncIterator(result, iterated).
    8. If ToBoolean(result) is true, return ? AsyncIteratorClose(iterated, NormalCompletion(value)).
    9. Set counter to counter + 1.

2.1.5.13 AsyncIterator.prototype [ @@toStringTag ]

The initial value of the @@toStringTag property is the String value "Async Iterator".

Note

Unlike the @@toStringTag on most built-in classes, for web-compatibility reasons this property must be writable.

3 New AsyncGenerator AOs

These factor out functionality from AsyncGenerator.prototype.next and AsyncGenerator.prototype.return and should be used in those methods when this proposal lands in the main specification.

3.1 AsyncGeneratorNext ( generator, brand, value )

The abstract operation AsyncGeneratorNext takes arguments generator, brand, and value (an ECMAScript language value) and returns an ECMAScript language value. It performs the following steps when called:

  1. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  2. Let result be Completion(AsyncGeneratorValidate(generator, brand)).
  3. IfAbruptRejectPromise(result, promiseCapability).
  4. Let state be generator.[[AsyncGeneratorState]].
  5. If state is completed, then
    1. Let iteratorResult be CreateIterResultObject(undefined, true).
    2. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »).
    3. Return promiseCapability.[[Promise]].
  6. Let completion be NormalCompletion(value).
  7. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability).
  8. If state is either suspendedStart or suspendedYield, then
    1. Perform AsyncGeneratorResume(generator, completion).
  9. Else,
    1. Assert: state is either executing or awaiting-return.
  10. Return promiseCapability.[[Promise]].

3.2 AsyncGeneratorReturn ( generator, brand, value )

The abstract operation AsyncGeneratorReturn takes arguments generator, brand, and value (an ECMAScript language value) and returns an ECMAScript language value. It performs the following steps when called:

  1. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  2. Let result be Completion(AsyncGeneratorValidate(generator, brand)).
  3. IfAbruptRejectPromise(result, promiseCapability).
  4. Let completion be Completion Record { [[Type]]: return, [[Value]]: value, [[Target]]: empty }.
  5. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability).
  6. Let state be generator.[[AsyncGeneratorState]].
  7. If state is either suspendedStart or completed, then
    1. Set generator.[[AsyncGeneratorState]] to awaiting-return.
    2. Perform ! AsyncGeneratorAwaitReturn(generator).
  8. Else if state is suspendedYield, then
    1. Perform AsyncGeneratorResume(generator, completion).
  9. Else,
    1. Assert: state is either executing or awaiting-return.
  10. Return promiseCapability.[[Promise]].