├── .gitignore ├── .github └── workflows │ ├── build.yml │ └── deploy.yml ├── package.json ├── DETAILS.md ├── README.md ├── proposal-iterator-helpers-biblio.json └── spec.html /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | 4 | .DS_Store 5 | Desktop.ini 6 | ._* 7 | Thumbs.db 8 | .Spotlight-V100 9 | .Trashes 10 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build spec 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-node@v3 16 | with: 17 | node-version: 16 18 | - run: npm ci 19 | - run: npm run build 20 | - run: npm run check-format 21 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy gh-pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: 16 17 | - run: npm ci 18 | - run: npm run build 19 | - uses: JamesIves/github-pages-deploy-action@v4.3.3 20 | with: 21 | branch: gh-pages 22 | folder: dist 23 | clean: true 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "mkdir -p dist && ecmarkup --lint-spec --strict --load-biblio @tc39/ecma262-biblio --load-biblio ./proposal-iterator-helpers-biblio.json --verbose --js-out dist/ecmarkup.js --css-out dist/ecmarkup.css spec.html dist/index.html", 5 | "format": "emu-format --write spec.html", 6 | "check-format": "emu-format --check spec.html" 7 | }, 8 | "devDependencies": { 9 | "@tc39/ecma262-biblio": "2.1.2407", 10 | "ecmarkup": "^15.0.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /DETAILS.md: -------------------------------------------------------------------------------- 1 | # Details 2 | 3 | There are number of decisions which could be made differently. This document attempts to catalog them along with the rationales for the choices currently made. 4 | 5 | ## Getting an iterator record from `this` 6 | 7 | The added methods on `%IteratorPrototype%` assume that `this` is an iterator, 8 | and therefore use a new `GetIteratorDirect` method to acquire an iterator 9 | record. This means that code like `Iterator.syncPrototype.map.call([1, 2, 3], ...)` 10 | will not work. 11 | 12 | ## Passing the protocol 13 | 14 | All added methods attempt to pass the values and calls they receive to whatever 15 | iterator they are wrapping. For example, `it.map(fn).next(5)` will call 16 | `it.next(5)` instead of `it.next()`. Additionally, calls like 17 | `it.map(fn).return()` will call upwards as well, to `it.return()`. 18 | 19 | ## Interface constraints 20 | 21 | - The interface used to expose these methods must not clash with existing APIs. 22 | For example: `Array.prototype.map` or `Map.prototype.forEach` denying access 23 | to the interface. 24 | - It must work with everywhere an iteration can occur. 25 | For example: `%GeneratorFunction%.prototype.map` will not work because the 26 | interface has to be obtained from an explicit function call. 27 | `%GeneratorFunction%.prototype` has no symbolic API to get the iterator. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Async Iterator Helpers 2 | 3 | A proposal for several interfaces that will help with general usage and 4 | consumption of async iterators in ECMAScript. 5 | 6 | This proposal was split out from [proposal-iterator-helpers](https://github.com/tc39/proposal-iterator-helpers) to resolve design questions related to concurrency (see below), which are not relevant to sync helpers. Many design questions (including choice of methods) were discussed and decided in that respository, and its readme should be read first. 7 | 8 | ## Status 9 | 10 | Authors: Gus Caplan, Michael Ficarra, Adam Vandolder, Jason Orendorff, Kevin Gibbons 11 | 12 | Champions: Michael Ficarra, Kevin Gibbons 13 | 14 | This proposal is at Stage 2 of [The TC39 Process](https://tc39.es/process-document/). 15 | 16 | **This proposal is in the process of being revised.** The core set of helpers and their high-level API is unlikely to change, but the underlying specification mechanism will likely be radically revised. 17 | 18 | This proposal contains the following methods: 19 | 20 | - `Iterator.prototype.toAsync` 21 | - `AsyncIterator.from` 22 | - `AsyncIterator.prototype` 23 | - `.map` 24 | - `.filter` 25 | - `.take` 26 | - `.drop` 27 | - `.flatMap` 28 | - `.reduce` 29 | - `.toArray` 30 | - `.forEach` 31 | - `.some` 32 | - `.every` 33 | - `.find` 34 | 35 | See [proposal-iterator-helpers](https://github.com/tc39/proposal-iterator-helpers) for motivation and additional high-level descriptions for these methods. 36 | 37 | ## Concurrency 38 | 39 | In the iterator-producing methods (`.map`, `.filter`, `.take`, `.drop`, and `.flatMap`), async helpers have the opportunity to support _concurrency_. For example, in the following code: 40 | 41 | ```js 42 | x = asyncIteratorOfUrls 43 | .map(u => fetch(u)) 44 | 45 | await Promise.all([ 46 | x.next(), 47 | x.next(), 48 | ]) 49 | ``` 50 | 51 | there could be two calls to `fetch` running at once. For this to work, the helpers have to be explicitly designed to support this. The original design of this proposal instead implemented the helpers as essentially async generators, which buffer calls to `.next` rather than allowing multiple calls to have simultaneous effects. 52 | 53 | This proposal is being revised to support at least the above use case. The exact details of what that looks like for each helper are not yet decided. 54 | 55 | ## How can I access the new intrinsics? 56 | 57 | This proposal introduces two new intrisic objects, in addition to the two added in the sync proposal. They can be accessed as follows: 58 | 59 | ```js 60 | const AsyncIteratorHelperPrototype = Object.getPrototypeOf(AsyncIterator.from([]).take(0)); 61 | const WrapForValidAsyncIteratorPrototype = Object.getPrototypeOf(AsyncIterator.from({ async next(){} })); 62 | ``` 63 | -------------------------------------------------------------------------------- /proposal-iterator-helpers-biblio.json: -------------------------------------------------------------------------------- 1 | {"location":"https://tc39.es/proposal-iterator-helpers","entries":[{"type":"table","id":"table-7","number":1,"caption":"Table 1: Well-Known Intrinsic Objects"},{"type":"clause","id":"sec-well-known-intrinsic-objects","aoid":null,"title":"Well-Known Intrinsic Objects","titleHTML":"Well-Known Intrinsic Objects","number":"1"},{"type":"op","aoid":"GetIteratorDirect","refId":"sec-getiteratordirect","kind":"abstract operation","signature":{"parameters":[{"name":"_obj_","type":{"kind":"opaque","type":"an ECMAScript language value"}}],"optionalParameters":[],"return":{"kind":"completion","completionType":"mixed","typeOfValueIfNormal":{"kind":"opaque","type":"an Iterator Record"}}},"effects":["user-code"]},{"type":"clause","id":"sec-getiteratordirect","aoid":"GetIteratorDirect","title":"GetIteratorDirect ( obj )","titleHTML":"GetIteratorDirect ( obj )","number":"2.1.1"},{"type":"op","aoid":"GetIteratorFlattenable","refId":"sec-getiteratorflattenable","kind":"abstract operation","signature":{"parameters":[{"name":"_obj_","type":{"kind":"opaque","type":"an ECMAScript language value"}},{"name":"_hint_","type":{"kind":"union","types":[{"kind":"opaque","type":"~sync~"},{"kind":"opaque","type":"~async~"}]}}],"optionalParameters":[],"return":{"kind":"completion","completionType":"mixed","typeOfValueIfNormal":{"kind":"opaque","type":"an Iterator Record"}}},"effects":["user-code"]},{"type":"clause","id":"sec-getiteratorflattenable","aoid":"GetIteratorFlattenable","title":"GetIteratorFlattenable ( obj, hint )","titleHTML":"GetIteratorFlattenable ( obj, hint )","number":"2.1.2"},{"type":"clause","id":"sec-operations-on-iterator-objects","aoid":null,"title":"Operations on Iterator Objects","titleHTML":"Operations on Iterator Objects","number":"2.1"},{"type":"clause","id":"sec-abstract-operations","aoid":null,"title":"Abstract Operations","titleHTML":"Abstract Operations","number":"2"},{"type":"term","term":"Iterator","refId":"sec-iterator-constructor"},{"type":"term","term":"%Iterator%","refId":"sec-iterator-constructor"},{"type":"clause","id":"sec-iterator","aoid":null,"title":"Iterator ( )","titleHTML":"Iterator ( )","number":"3.1.1.1.1"},{"type":"clause","id":"sec-iterator-constructor","aoid":null,"title":"The Iterator Constructor","titleHTML":"The Iterator Constructor","number":"3.1.1.1"},{"type":"clause","id":"sec-iterator.prototype","aoid":null,"title":"Iterator.prototype","titleHTML":"Iterator.prototype","number":"3.1.1.2.1"},{"type":"term","term":"%WrapForValidIteratorPrototype%","refId":"sec-wrapforvaliditeratorprototype-object"},{"type":"clause","id":"sec-wrapforvaliditeratorprototype.next","aoid":null,"title":"%WrapForValidIteratorPrototype%.next ( )","titleHTML":"%WrapForValidIteratorPrototype%.next ( )","number":"3.1.1.2.2.1.1"},{"type":"clause","id":"sec-wrapforvaliditeratorprototype.return","aoid":null,"title":"%WrapForValidIteratorPrototype%.return ( )","titleHTML":"%WrapForValidIteratorPrototype%.return ( )","number":"3.1.1.2.2.1.2"},{"type":"clause","id":"sec-wrapforvaliditeratorprototype-object","aoid":null,"title":"The %WrapForValidIteratorPrototype% Object","titleHTML":"The %WrapForValidIteratorPrototype% Object","number":"3.1.1.2.2.1"},{"type":"clause","id":"sec-iterator.from","aoid":null,"title":"Iterator.from ( O )","titleHTML":"Iterator.from ( O )","number":"3.1.1.2.2"},{"type":"clause","id":"sec-properties-of-the-iterator-constructor","aoid":null,"title":"Properties of the Iterator Constructor","titleHTML":"Properties of the Iterator Constructor","number":"3.1.1.2"},{"type":"clause","id":"sec-iterator-objects","aoid":null,"title":"Iterator Objects","titleHTML":"Iterator Objects","number":"3.1.1"},{"type":"term","term":"%IteratorHelperPrototype%","refId":"sec-%iteratorhelperprototype%-object"},{"type":"clause","id":"sec-%iteratorhelperprototype%.next","aoid":null,"title":"%IteratorHelperPrototype%.next ( )","titleHTML":"%IteratorHelperPrototype%.next ( )","number":"3.1.2.1.1"},{"type":"clause","id":"sec-%iteratorhelperprototype%.return","aoid":null,"title":"%IteratorHelperPrototype%.return ( )","titleHTML":"%IteratorHelperPrototype%.return ( )","number":"3.1.2.1.2"},{"type":"clause","id":"sec-%iteratorhelperprototype%-@@tostringtag","aoid":null,"title":"%IteratorHelperPrototype% [ @@toStringTag ]","titleHTML":"%IteratorHelperPrototype% [ @@toStringTag ]","number":"3.1.2.1.3"},{"type":"clause","id":"sec-%iteratorhelperprototype%-object","aoid":null,"title":"The %IteratorHelperPrototype% Object","titleHTML":"The %IteratorHelperPrototype% Object","number":"3.1.2.1"},{"type":"clause","id":"sec-iterator-helper-objects","aoid":null,"title":"Iterator Helper Objects","titleHTML":"Iterator Helper Objects","number":"3.1.2"},{"type":"term","term":"Iterator prototype object","refId":"sec-iteratorprototype"},{"type":"term","term":"%Iterator.prototype%","refId":"sec-iteratorprototype"},{"type":"clause","id":"sec-iteratorprototype.constructor","aoid":null,"title":"Iterator.prototype.constructor","titleHTML":"Iterator.prototype.constructor","number":"3.1.3.1"},{"type":"clause","id":"sec-iteratorprototype.map","aoid":null,"title":"Iterator.prototype.map ( mapper )","titleHTML":"Iterator.prototype.map ( mapper )","number":"3.1.3.2"},{"type":"clause","id":"sec-iteratorprototype.filter","aoid":null,"title":"Iterator.prototype.filter ( predicate )","titleHTML":"Iterator.prototype.filter ( predicate )","number":"3.1.3.3"},{"type":"clause","id":"sec-iteratorprototype.take","aoid":null,"title":"Iterator.prototype.take ( limit )","titleHTML":"Iterator.prototype.take ( limit )","number":"3.1.3.4"},{"type":"clause","id":"sec-iteratorprototype.drop","aoid":null,"title":"Iterator.prototype.drop ( limit )","titleHTML":"Iterator.prototype.drop ( limit )","number":"3.1.3.5"},{"type":"clause","id":"sec-iteratorprototype.flatmap","aoid":null,"title":"Iterator.prototype.flatMap ( mapper )","titleHTML":"Iterator.prototype.flatMap ( mapper )","number":"3.1.3.6"},{"type":"clause","id":"sec-iteratorprototype.reduce","aoid":null,"title":"Iterator.prototype.reduce ( reducer [ , initialValue ] )","titleHTML":"Iterator.prototype.reduce ( reducer [ , initialValue ] )","number":"3.1.3.7"},{"type":"clause","id":"sec-iteratorprototype.toarray","aoid":null,"title":"Iterator.prototype.toArray ( )","titleHTML":"Iterator.prototype.toArray ( )","number":"3.1.3.8"},{"type":"clause","id":"sec-iteratorprototype.foreach","aoid":null,"title":"Iterator.prototype.forEach ( fn )","titleHTML":"Iterator.prototype.forEach ( fn )","number":"3.1.3.9"},{"type":"clause","id":"sec-iteratorprototype.some","aoid":null,"title":"Iterator.prototype.some ( predicate )","titleHTML":"Iterator.prototype.some ( predicate )","number":"3.1.3.10"},{"type":"clause","id":"sec-iteratorprototype.every","aoid":null,"title":"Iterator.prototype.every ( predicate )","titleHTML":"Iterator.prototype.every ( predicate )","number":"3.1.3.11"},{"type":"clause","id":"sec-iteratorprototype.find","aoid":null,"title":"Iterator.prototype.find ( predicate )","titleHTML":"Iterator.prototype.find ( predicate )","number":"3.1.3.12"},{"type":"clause","id":"sec-iteratorprototype-@@tostringtag","aoid":null,"title":"Iterator.prototype [ @@toStringTag ]","titleHTML":"Iterator.prototype [ @@toStringTag ]","number":"3.1.3.13"},{"type":"clause","id":"sec-iteratorprototype","aoid":null,"title":"Iterator.prototype","titleHTML":"Iterator.prototype","number":"3.1.3"},{"type":"clause","id":"sec-iteration","aoid":null,"title":"Iteration","titleHTML":"Iteration","number":"3.1"},{"type":"clause","id":"sec-control-abstraction-objects","aoid":null,"title":"Control Abstraction Objects","titleHTML":"Control Abstraction Objects","number":"3"}]} -------------------------------------------------------------------------------- /spec.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
  4 | title: Async Iterator Helpers
  5 | status: proposal
  6 | stage: 2
  7 | location: https://tc39.es/proposal-async-iterator-helpers
  8 | copyright: false
  9 | contributors: Gus Caplan, Michael Ficarra, Kevin Gibbons
 10 | 
11 | 45 | 46 |
47 |

Contributing to this Proposal

48 |

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:

49 | 61 |
62 | 63 | 64 |

Well-Known Intrinsic Objects

65 | 66 | 67 | 68 | 69 | 72 | 75 | 78 | 79 | 80 | 83 | 86 | 89 | 90 | 91 | 94 | 97 | 101 | 102 |
70 | Intrinsic Name 71 | 73 | Global Name 74 | 76 | ECMAScript Language Association 77 |
81 | %AsyncIterator% 82 | 84 | `AsyncIterator` 85 | 87 | The `AsyncIterator` constructor () 88 |
92 | %AsyncIteratorPrototype% 93 | 95 | `AsyncIterator.prototype` 96 | 98 | An object that all standard built-in async iterator objects indirectly inherit from 99 |

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

100 |
103 |
104 |
105 | 106 | 107 |

Control Abstraction Objects

108 | 109 | 110 |

Iteration

111 | 112 | 113 |

Iterator Abstract Operations

114 | 115 | 116 |

IfAbruptCloseAsyncIterator ( _value_, _iteratorRecord_ )

117 |

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

118 | 119 | 1. IfAbruptCloseAsyncIterator(_value_, _iteratorRecord_). 120 | 121 |

means the same thing as:

122 | 123 | 1. If _value_ is an abrupt completion, then 124 | 1. Perform ? AsyncIteratorClose(_iteratorRecord_, _value_). 125 | 1. Return _value_. 126 | 1. Else if _value_ is a Completion Record, set _value_ to _value_.[[Value]]. 127 | 128 |
129 | 130 | 131 |

132 | AwaitNonPrimitive ( 133 | _value_: an ECMAScript language value, 134 | ): either a normal completion containing an ECMAScript language value or a throw completion 135 |

136 |
137 |
138 | 139 | 1. If _value_ is an Object, return ? Await(_value_). 140 | 1. Else, return _value_. 141 | 142 |
143 |
144 | 145 | 146 |

AsyncIterator Objects

147 | 148 | 149 |

The AsyncIterator Constructor

150 |

The AsyncIterator constructor:

151 |
    152 |
  • is %AsyncIterator%.
  • 153 |
  • is the initial value of the *"AsyncIterator"* property of the global object.
  • 154 |
  • is designed to be subclassable. It may be used as the value of an *extends* clause of a class definition.
  • 155 |
156 | 157 | 158 |

AsyncIterator ( )

159 |

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

160 | 161 | 1. If NewTarget is *undefined* or the active function object, throw a *TypeError* exception. 162 | 1. Return ? OrdinaryCreateFromConstructor(NewTarget, *"%AsyncIterator.prototype%"*). 163 | 164 |
165 |
166 | 167 | 168 |

Properties of the AsyncIterator Constructor

169 | 170 | 171 |

AsyncIterator.prototype

172 |

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

173 |

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

174 |
175 | 176 | 177 |

AsyncIterator.from ( _O_ )

178 | 179 | 1. If _O_ is a String, set _O_ to ! ToObject(_O_). 180 | 1. Let _iteratorRecord_ be ? GetIteratorFlattenable(_O_, ~async~). 181 | 1. Let _hasInstance_ be ? OrdinaryHasInstance(%AsyncIterator%, _iteratorRecord_.[[Iterator]]). 182 | 1. If _hasInstance_ is *true*, then 183 | 1. Return _iteratorRecord_.[[Iterator]]. 184 | 1. Let _wrapper_ be OrdinaryObjectCreate(%WrapForValidAsyncIteratorPrototype%, « [[AsyncIterated]] »). 185 | 1. Set _wrapper_.[[AsyncIterated]] to _iteratorRecord_. 186 | 1. Return _wrapper_. 187 | 188 | 189 | 190 |

The %WrapForValidAsyncIteratorPrototype% Object

191 |

The %WrapForValidAsyncIteratorPrototype% object:

192 | 193 |
    194 |
  • is an ordinary object.
  • 195 |
  • has a [[Prototype]] internal slot whose value is %AsyncIterator.prototype%.
  • 196 |
  • has the following properties:
  • 197 |
198 | 199 | 200 |

%WrapForValidAsyncIteratorPrototype%.next ( )

201 | 202 | 1. Let _O_ be *this* value. 203 | 1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%). 204 | 1. Let _check_ be Completion(RequireInternalSlot(_O_, [[AsyncIterated]])). 205 | 1. IfAbruptRejectPromise(_check_, _promiseCapability_). 206 | 1. Let _result_ be Completion(IteratorNext(_O_.[[AsyncIterated]])). 207 | 1. IfAbruptRejectPromise(_result_, _promiseCapability_). 208 | 1. Return ? PromiseResolve(%Promise%, _result_). 209 | 210 |
211 | 212 | 213 |

%WrapForValidAsyncIteratorPrototype%.return ( )

214 | 215 | 1. Let _O_ be *this* value. 216 | 1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%). 217 | 1. Let _check_ be Completion(RequireInternalSlot(_O_, [[AsyncIterated]])). 218 | 1. IfAbruptRejectPromise(_check_, _promiseCapability_). 219 | 1. Let _iterator_ be _O_.[[AsyncIterated]].[[Iterator]]. 220 | 1. Assert: _iterator_ is an Object. 221 | 1. Let _returnMethod_ be Completion(GetMethod(_iterator_, *"return"*)). 222 | 1. IfAbruptRejectPromise(_returnMethod_, _promiseCapability_). 223 | 1. If _returnMethod_ is *undefined*, then 224 | 1. Return ! PromiseResolve(%Promise%, CreateIterResultObject(*undefined*, *true*)). 225 | 1. Let _result_ be Completion(Call(_returnMethod_, _iterator_)). 226 | 1. IfAbruptRejectPromise(_result_, _promiseCapability_). 227 | 1. Return ? PromiseResolve(%Promise%, _result_). 228 | 229 |
230 |
231 |
232 |
233 |
234 | 235 | 236 |

Async Iterator Helper Objects

237 |

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.

238 | 239 | 240 |

The %AsyncIteratorHelperPrototype% Object

241 |

The %AsyncIteratorHelperPrototype% object:

242 |
    243 |
  • has properties that are inherited by all Async Iterator Helper Objects.
  • 244 |
  • is an ordinary object.
  • 245 |
  • has a [[Prototype]] internal slot whose value is %AsyncIterator.prototype%.
  • 246 |
  • has the following properties:
  • 247 |
248 | 249 | 250 |

%AsyncIteratorHelperPrototype%.next ( )

251 | 252 | 1. Return AsyncGeneratorNext(*this* value, *"Async Iterator Helper"*, *undefined*). 253 | 254 |
255 | 256 | 257 |

%AsyncIteratorHelperPrototype%.return ( )

258 | 259 | 1. Return AsyncGeneratorReturn(*this* value, *"Async Iterator Helper"*, *undefined*). 260 | 261 |
262 | 263 | 264 |

%AsyncIteratorHelperPrototype% [ @@toStringTag ]

265 |

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

266 |

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

267 |
268 |
269 |
270 | 271 | 272 |

Iterator.prototype

273 | 274 | 275 |

Iterator.prototype.toAsync ( )

276 |

This method performs the following steps when called:

277 | 278 | 1. Let _syncIteratorRecord_ be ? GetIteratorDirect(*this* value). 279 | 1. Let _asyncIteratorRecord_ be CreateAsyncFromSyncIterator(_syncIteratorRecord_). 280 | 1. Let _wrapper_ be OrdinaryObjectCreate(%WrapForValidAsyncIteratorPrototype%, « [[AsyncIterated]] »). 281 | 1. Set _wrapper_.[[AsyncIterated]] to _asyncIteratorRecord_. 282 | 1. Return _wrapper_. 283 | 284 |
285 |
286 | 287 | 288 |

AsyncIterator.prototype

289 |

The AsyncIterator prototype object:

290 |
    291 |
  • is %AsyncIterator.prototype%.
  • 292 |
  • has a [[Prototype]] internal slot whose value is %Object.prototype%.
  • 293 |
  • is an ordinary object.
  • 294 |
295 | 296 | 297 |

AsyncIterator.prototype.constructor

298 |

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

299 |
300 | 301 | 302 |

AsyncIterator.prototype.map ( _mapper_ )

303 |

This method performs the following steps when called:

304 | 305 | 1. Let _iterated_ be ? GetIteratorDirect(*this* value). 306 | 1. If IsCallable(_mapper_) is *false*, throw a *TypeError* exception. 307 | 1. Let _closure_ be a new Abstract Closure with no parameters that captures _iterated_ and _mapper_ and performs the following steps when called: 308 | 1. Let _counter_ be 0. 309 | 1. Repeat, 310 | 1. Let _next_ be ? Await(? IteratorNext(_iterated_)). 311 | 1. If ? IteratorComplete(_next_) is *true*, return *undefined*. 312 | 1. Let _value_ be ? IteratorValue(_next_). 313 | 1. Let _mapped_ be Completion(Call(_mapper_, *undefined*, « _value_, 𝔽(_counter_) »)). 314 | 1. IfAbruptCloseAsyncIterator(_mapped_, _iterated_). 315 | 1. Set _mapped_ to Completion(AwaitNonPrimitive(_mapped_)). 316 | 1. IfAbruptCloseAsyncIterator(_mapped_, _iterated_). 317 | 1. Let _completion_ be Completion(Yield(_mapped_)). 318 | 1. IfAbruptCloseAsyncIterator(_completion_, _iterated_). 319 | 1. Set _counter_ to _counter_ + 1. 320 | 1. Return CreateAsyncIteratorFromClosure(_closure_, *"Async Iterator Helper"*, %AsyncIteratorHelperPrototype%). 321 | 322 |
323 | 324 | 325 |

AsyncIterator.prototype.filter ( _predicate_ )

326 |

This method performs the following steps when called:

327 | 328 | 1. Let _iterated_ be ? GetIteratorDirect(*this* value). 329 | 1. If IsCallable(_predicate_) is *false*, throw a *TypeError* exception. 330 | 1. Let _closure_ be a new Abstract Closure with no parameters that captures _iterated_ and _predicate_ and performs the following steps when called: 331 | 1. Let _counter_ be 0. 332 | 1. Repeat, 333 | 1. Let _next_ be ? Await(? IteratorNext(_iterated_)). 334 | 1. If ? IteratorComplete(_next_) is *true*, return *undefined*. 335 | 1. Let _value_ be ? IteratorValue(_next_). 336 | 1. Let _selected_ be Completion(Call(_predicate_, *undefined*, « _value_, 𝔽(_counter_) »)). 337 | 1. IfAbruptCloseAsyncIterator(_selected_, _iterated_). 338 | 1. Set _selected_ to Completion(AwaitNonPrimitive(_selected_)). 339 | 1. IfAbruptCloseAsyncIterator(_selected_, _iterated_). 340 | 1. If ToBoolean(_selected_) is *true*, then 341 | 1. Let _completion_ be Completion(Yield(_value_)). 342 | 1. IfAbruptCloseAsyncIterator(_completion_, _iterated_). 343 | 1. Set _counter_ to _counter_ + 1. 344 | 1. Return CreateAsyncIteratorFromClosure(_closure_, *"Async Iterator Helper"*, %AsyncIteratorHelperPrototype%). 345 | 346 |
347 | 348 | 349 |

AsyncIterator.prototype.take ( _limit_ )

350 |

This method performs the following steps when called:

351 | 352 | 1. Let _iterated_ be ? GetIteratorDirect(*this* value). 353 | 1. Let _numLimit_ be ? ToNumber(_limit_). 354 | 1. If _numLimit_ is *NaN*, throw a *RangeError* exception. 355 | 1. Let _integerLimit_ be ! ToIntegerOrInfinity(_numLimit_). 356 | 1. If _integerLimit_ < 0, throw a *RangeError* exception. 357 | 1. Let _closure_ be a new Abstract Closure with no parameters that captures _iterated_ and _integerLimit_ and performs the following steps when called: 358 | 1. Let _remaining_ be _integerLimit_. 359 | 1. Repeat, 360 | 1. If _remaining_ is 0, then 361 | 1. Return ? AsyncIteratorClose(_iterated_, NormalCompletion(*undefined*)). 362 | 1. If _remaining_ is not +∞, then 363 | 1. Set _remaining_ to _remaining_ - 1. 364 | 1. Let _next_ be ? Await(? IteratorNext(_iterated_)). 365 | 1. If ? IteratorComplete(_next_) is *true*, return *undefined*. 366 | 1. Let _completion_ be Completion(Yield(? IteratorValue(_next_))). 367 | 1. IfAbruptCloseAsyncIterator(_completion_, _iterated_). 368 | 1. Return CreateAsyncIteratorFromClosure(_closure_, *"Async Iterator Helper"*, %AsyncIteratorHelperPrototype%). 369 | 370 |
371 | 372 | 373 |

AsyncIterator.prototype.drop ( _limit_ )

374 |

This method performs the following steps when called:

375 | 376 | 1. Let _iterated_ be ? GetIteratorDirect(*this* value). 377 | 1. Let _numLimit_ be ? ToNumber(_limit_). 378 | 1. If _numLimit_ is *NaN*, throw a *RangeError* exception. 379 | 1. Let _integerLimit_ be ! ToIntegerOrInfinity(_numLimit_). 380 | 1. If _integerLimit_ < 0, throw a *RangeError* exception. 381 | 1. Let _closure_ be a new Abstract Closure with no parameters that captures _iterated_ and _integerLimit_ and performs the following steps when called: 382 | 1. Let _remaining_ be _integerLimit_. 383 | 1. Repeat, while _remaining_ > 0, 384 | 1. If _remaining_ is not +∞, then 385 | 1. Set _remaining_ to _remaining_ - 1. 386 | 1. Let _next_ be ? Await(? IteratorNext(_iterated_)). 387 | 1. If ? IteratorComplete(_next_) is *true*, return *undefined*. 388 | 1. Repeat, 389 | 1. Let _next_ be ? Await(? IteratorNext(_iterated_)). 390 | 1. If ? IteratorComplete(_next_) is *true*, return *undefined*. 391 | 1. Let _completion_ be Completion(Yield(? IteratorValue(_next_))). 392 | 1. IfAbruptCloseAsyncIterator(_completion_, _iterated_). 393 | 1. Return CreateAsyncIteratorFromClosure(_closure_, *"Async Iterator Helper"*, %AsyncIteratorHelperPrototype%). 394 | 395 |
396 | 397 | 398 |

AsyncIterator.prototype.flatMap ( _mapper_ )

399 |

This method performs the following steps when called:

400 | 401 | 1. Let _iterated_ be ? GetIteratorDirect(*this* value). 402 | 1. If IsCallable(_mapper_) is *false*, throw a *TypeError* exception. 403 | 1. Let _closure_ be a new Abstract Closure with no parameters that captures _iterated_ and _mapper_ and performs the following steps when called: 404 | 1. Let _counter_ be 0. 405 | 1. Repeat, 406 | 1. Let _next_ be ? Await(? IteratorNext(_iterated_)). 407 | 1. If ? IteratorComplete(_next_) is *true*, return *undefined*. 408 | 1. Let _value_ be ? IteratorValue(_next_). 409 | 1. Let _mapped_ be Completion(Call(_mapper_, *undefined*, « _value_, 𝔽(_counter_) »)). 410 | 1. IfAbruptCloseAsyncIterator(_mapped_, _iterated_). 411 | 1. Set _mapped_ to Completion(AwaitNonPrimitive(_mapped_)). 412 | 1. IfAbruptCloseAsyncIterator(_mapped_, _iterated_). 413 | 1. Let _innerIterator_ be Completion(GetIteratorFlattenable(_mapped_, ~async~)). 414 | 1. IfAbruptCloseAsyncIterator(_innerIterator_, _iterated_). 415 | 1. Let _innerAlive_ be *true*. 416 | 1. Repeat, while _innerAlive_ is *true*, 417 | 1. Let _innerNextPromise_ be Completion(IteratorNext(_innerIterator_)). 418 | 1. IfAbruptCloseAsyncIterator(_innerNextPromise_, _iterated_). 419 | 1. Let _innerNext_ be Completion(Await(_innerNextPromise_)). 420 | 1. IfAbruptCloseAsyncIterator(_innerNext_, _iterated_). 421 | 1. Let _innerComplete_ be Completion(IteratorComplete(_innerNext_)). 422 | 1. IfAbruptCloseAsyncIterator(_innerComplete_, _iterated_). 423 | 1. If _innerComplete_ is *true*, then 424 | 1. Set _innerAlive_ to *false*. 425 | 1. Else, 426 | 1. Let _innerValue_ be Completion(IteratorValue(_innerNext_)). 427 | 1. IfAbruptCloseAsyncIterator(_innerValue_, _iterated_). 428 | 1. [id="step-async-iterator-flatmap-yield"] Let _completion_ be Completion(Yield(_innerValue_)). 429 | 1. If _completion_ is a return completion, then 430 | 1. Let _backupCompletion_ be Completion(IteratorClose(_innerIterator_, _completion_)). 431 | 1. IfAbruptCloseIterator(_backupCompletion_, _iterated_). 432 | 1. Return ? IteratorClose(_completion_, _iterated_). 433 | 1. Else if _completion_ is a throw completion, then 434 | 1. Assert: Awaiting _innerValue_ during the Yield on step threw. 435 | 1. Return ? IteratorClose(_completion_, _iterated_). 436 | 1. Set _counter_ to _counter_ + 1. 437 | 1. Return CreateAsyncIteratorFromClosure(_closure_, *"Async Iterator Helper"*, %AsyncIteratorHelperPrototype%). 438 | 439 |
440 | 441 | 442 |

AsyncIterator.prototype.reduce ( _reducer_ [ , _initialValue_ ] )

443 |

This async method performs the following steps when called:

444 | 445 | 1. Let _iterated_ be ? GetIteratorDirect(*this* value). 446 | 1. If IsCallable(_reducer_) is *false*, throw a *TypeError* exception. 447 | 1. If _initialValue_ is not present, then 448 | 1. Let _next_ be ? Await(? IteratorNext(_iterated_)). 449 | 1. If ? IteratorComplete(_next_) is *true*, throw a *TypeError* exception. 450 | 1. Let _accumulator_ be ? IteratorValue(_next_). 451 | 1. Let _counter_ be 1. 452 | 1. Else, 453 | 1. Let _accumulator_ be _initialValue_. 454 | 1. Let _counter_ be 0. 455 | 1. Repeat, 456 | 1. Let _next_ be ? Await(? IteratorNext(_iterated_)). 457 | 1. If ? IteratorComplete(_next_) is *true*, return _accumulator_. 458 | 1. Let _value_ be ? IteratorValue(_next_). 459 | 1. Let _result_ be Completion(Call(_reducer_, *undefined*, « _accumulator_, _value_, 𝔽(_counter_) »)). 460 | 1. IfAbruptCloseAsyncIterator(_result_, _iterated_). 461 | 1. Set _result_ to Completion(AwaitNonPrimitive(_result_)). 462 | 1. IfAbruptCloseAsyncIterator(_result_, _iterated_). 463 | 1. Set _accumulator_ to _result_. 464 | 1. Set _counter_ to _counter_ + 1. 465 | 466 |
467 | 468 | 469 |

AsyncIterator.prototype.toArray ( )

470 |

This async method performs the following steps when called:

471 | 472 | 1. Let _iterated_ be ? GetIteratorDirect(*this* value). 473 | 1. Let _items_ be a new empty List. 474 | 1. Repeat, 475 | 1. Let _next_ be ? Await(? IteratorNext(_iterated_)). 476 | 1. If ? IteratorComplete(_next_) is *true*, return CreateArrayFromList(_items_). 477 | 1. Let _value_ be ? IteratorValue(_next_). 478 | 1. Append _value_ to _items_. 479 | 480 |
481 | 482 | 483 |

AsyncIterator.prototype.forEach ( _fn_ )

484 |

This async method performs the following steps when called:

485 | 486 | 1. Let _iterated_ be ? GetIteratorDirect(*this* value). 487 | 1. If IsCallable(_fn_) is *false*, throw a *TypeError* exception. 488 | 1. Let _counter_ be 0. 489 | 1. Repeat, 490 | 1. Let _next_ be ? Await(? IteratorNext(_iterated_)). 491 | 1. If ? IteratorComplete(_next_) is *true*, return *undefined*. 492 | 1. Let _value_ be ? IteratorValue(_next_). 493 | 1. Let _r_ be Completion(Call(_fn_, *undefined*, « _value_, 𝔽(_counter_) »)). 494 | 1. IfAbruptCloseAsyncIterator(_r_, _iterated_). 495 | 1. Set _r_ to Completion(AwaitNonPrimitive(r)). 496 | 1. IfAbruptCloseAsyncIterator(_r_, _iterated_). 497 | 1. Set _counter_ to _counter_ + 1. 498 | 499 |
500 | 501 | 502 |

AsyncIterator.prototype.some ( _predicate_ )

503 |

This async method performs the following steps when called:

504 | 505 | 1. Let _iterated_ be ? GetIteratorDirect(*this* value). 506 | 1. If IsCallable(_predicate_) is *false*, throw a *TypeError* exception. 507 | 1. Let _counter_ be 0. 508 | 1. Repeat, 509 | 1. Let _next_ be ? Await(? IteratorNext(_iterated_)). 510 | 1. If ? IteratorComplete(_next_) is *true*, return *false*. 511 | 1. Let _value_ be ? IteratorValue(_next_). 512 | 1. Let _result_ be Completion(Call(_predicate_, *undefined*, « _value_, 𝔽(_counter_) »)). 513 | 1. IfAbruptCloseAsyncIterator(_result_, _iterated_). 514 | 1. Set _result_ to Completion(AwaitNonPrimitive(_result_)). 515 | 1. IfAbruptCloseAsyncIterator(_result_, _iterated_). 516 | 1. If ToBoolean(_result_) is *true*, return ? AsyncIteratorClose(_iterated_, NormalCompletion(*true*)). 517 | 1. Set _counter_ to _counter_ + 1. 518 | 519 |
520 | 521 | 522 |

AsyncIterator.prototype.every ( _predicate_ )

523 |

This async method performs the following steps when called:

524 | 525 | 1. Let _iterated_ be ? GetIteratorDirect(*this* value). 526 | 1. If IsCallable(_predicate_) is *false*, throw a *TypeError* exception. 527 | 1. Let _counter_ be 0. 528 | 1. Repeat, 529 | 1. Let _next_ be ? Await(? IteratorNext(_iterated_)). 530 | 1. If ? IteratorComplete(_next_) is *true*, return *true*. 531 | 1. Let _value_ be ? IteratorValue(_next_). 532 | 1. Let _result_ be Completion(Call(_predicate_, *undefined*, « _value_, 𝔽(_counter_) »)). 533 | 1. IfAbruptCloseAsyncIterator(_result_, _iterated_). 534 | 1. Set _result_ to Completion(AwaitNonPrimitive(_result_)). 535 | 1. IfAbruptCloseAsyncIterator(_result_, _iterated_). 536 | 1. If ToBoolean(_result_) is *false*, return ? AsyncIteratorClose(_iterated_, NormalCompletion(*false*)). 537 | 1. Set _counter_ to _counter_ + 1. 538 | 539 |
540 | 541 | 542 |

AsyncIterator.prototype.find ( _predicate_ )

543 |

This async method performs the following steps when called:

544 | 545 | 1. Let _iterated_ be ? GetIteratorDirect(*this* value). 546 | 1. If IsCallable(_predicate_) is *false*, throw a *TypeError* exception. 547 | 1. Let _counter_ be 0. 548 | 1. Repeat, 549 | 1. Let _next_ be ? Await(? IteratorNext(_iterated_)). 550 | 1. If ? IteratorComplete(_next_) is *true*, return *undefined*. 551 | 1. Let _value_ be ? IteratorValue(_next_). 552 | 1. Let _result_ be Completion(Call(_predicate_, *undefined*, « _value_, 𝔽(_counter_) »)). 553 | 1. IfAbruptCloseAsyncIterator(_result_, _iterated_). 554 | 1. Set _result_ to Completion(AwaitNonPrimitive(_result_)). 555 | 1. IfAbruptCloseAsyncIterator(_result_, _iterated_). 556 | 1. If ToBoolean(_result_) is *true*, return ? AsyncIteratorClose(_iterated_, NormalCompletion(_value_)). 557 | 1. Set _counter_ to _counter_ + 1. 558 | 559 |
560 | 561 | 562 |

AsyncIterator.prototype [ @@toStringTag ]

563 |

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

564 | 565 |

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

566 |
567 |
568 |
569 |
570 |
571 | 572 | 573 |

New AsyncGenerator AOs

574 |

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.

575 | 576 | 577 |

578 | AsyncGeneratorNext ( 579 | _generator_: unknown, 580 | _brand_: unknown, 581 | _value_: an ECMAScript language value, 582 | ): an ECMAScript language value 583 |

584 |
585 |
586 | 587 | 1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%). 588 | 1. Let _result_ be Completion(AsyncGeneratorValidate(_generator_, _brand_)). 589 | 1. IfAbruptRejectPromise(_result_, _promiseCapability_). 590 | 1. Let _state_ be _generator_.[[AsyncGeneratorState]]. 591 | 1. If _state_ is ~completed~, then 592 | 1. Let _iteratorResult_ be CreateIterResultObject(*undefined*, *true*). 593 | 1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _iteratorResult_ »). 594 | 1. Return _promiseCapability_.[[Promise]]. 595 | 1. Let _completion_ be NormalCompletion(_value_). 596 | 1. Perform AsyncGeneratorEnqueue(_generator_, _completion_, _promiseCapability_). 597 | 1. If _state_ is either ~suspendedStart~ or ~suspendedYield~, then 598 | 1. Perform AsyncGeneratorResume(_generator_, _completion_). 599 | 1. Else, 600 | 1. Assert: _state_ is either ~executing~ or ~awaiting-return~. 601 | 1. Return _promiseCapability_.[[Promise]]. 602 | 603 |
604 | 605 | 606 |

607 | AsyncGeneratorReturn ( 608 | _generator_: unknown, 609 | _brand_: unknown, 610 | _value_: an ECMAScript language value, 611 | ): an ECMAScript language value 612 |

613 |
614 |
615 | 616 | 1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%). 617 | 1. Let _result_ be Completion(AsyncGeneratorValidate(_generator_, _brand_)). 618 | 1. IfAbruptRejectPromise(_result_, _promiseCapability_). 619 | 1. Let _completion_ be Completion Record { [[Type]]: ~return~, [[Value]]: _value_, [[Target]]: ~empty~ }. 620 | 1. Perform AsyncGeneratorEnqueue(_generator_, _completion_, _promiseCapability_). 621 | 1. Let _state_ be _generator_.[[AsyncGeneratorState]]. 622 | 1. If _state_ is either ~suspendedStart~ or ~completed~, then 623 | 1. Set _generator_.[[AsyncGeneratorState]] to ~awaiting-return~. 624 | 1. Perform ! AsyncGeneratorAwaitReturn(_generator_). 625 | 1. Else if _state_ is ~suspendedYield~, then 626 | 1. Perform AsyncGeneratorResume(_generator_, _completion_). 627 | 1. Else, 628 | 1. Assert: _state_ is either ~executing~ or ~awaiting-return~. 629 | 1. Return _promiseCapability_.[[Promise]]. 630 | 631 |
632 |
633 | --------------------------------------------------------------------------------