index.mjs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. /**
  2. * Copyright (c) 2016, Lee Byron
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the MIT license found in the
  6. * LICENSE file in the root directory of this source tree.
  7. *
  8. * @flow
  9. * @ignore
  10. */
  11. /**
  12. * [Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#iterator)
  13. * is a *protocol* which describes a standard way to produce a sequence of
  14. * values, typically the values of the Iterable represented by this Iterator.
  15. *
  16. * While described by the [ES2015 version of JavaScript](http://www.ecma-international.org/ecma-262/6.0/#sec-iterator-interface)
  17. * it can be utilized by any version of JavaScript.
  18. *
  19. * @external Iterator
  20. * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#iterator|MDN Iteration protocols}
  21. */
  22. /**
  23. * [Iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#iterable)
  24. * is a *protocol* which when implemented allows a JavaScript object to define
  25. * their iteration behavior, such as what values are looped over in a
  26. * [`for...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of)
  27. * loop or `iterall`'s `forEach` function. Many [built-in types](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#Builtin_iterables)
  28. * implement the Iterable protocol, including `Array` and `Map`.
  29. *
  30. * While described by the [ES2015 version of JavaScript](http://www.ecma-international.org/ecma-262/6.0/#sec-iterable-interface)
  31. * it can be utilized by any version of JavaScript.
  32. *
  33. * @external Iterable
  34. * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#iterable|MDN Iteration protocols}
  35. */
  36. // In ES2015 environments, Symbol exists
  37. var SYMBOL /*: any */ = typeof Symbol === 'function' ? Symbol : void 0
  38. // In ES2015 (or a polyfilled) environment, this will be Symbol.iterator
  39. var SYMBOL_ITERATOR = SYMBOL && SYMBOL.iterator
  40. /**
  41. * A property name to be used as the name of an Iterable's method responsible
  42. * for producing an Iterator, referred to as `@@iterator`. Typically represents
  43. * the value `Symbol.iterator` but falls back to the string `"@@iterator"` when
  44. * `Symbol.iterator` is not defined.
  45. *
  46. * Use `$$iterator` for defining new Iterables instead of `Symbol.iterator`,
  47. * but do not use it for accessing existing Iterables, instead use
  48. * {@link getIterator} or {@link isIterable}.
  49. *
  50. * @example
  51. *
  52. * var $$iterator = require('iterall').$$iterator
  53. *
  54. * function Counter (to) {
  55. * this.to = to
  56. * }
  57. *
  58. * Counter.prototype[$$iterator] = function () {
  59. * return {
  60. * to: this.to,
  61. * num: 0,
  62. * next () {
  63. * if (this.num >= this.to) {
  64. * return { value: undefined, done: true }
  65. * }
  66. * return { value: this.num++, done: false }
  67. * }
  68. * }
  69. * }
  70. *
  71. * var counter = new Counter(3)
  72. * for (var number of counter) {
  73. * console.log(number) // 0 ... 1 ... 2
  74. * }
  75. *
  76. * @type {Symbol|string}
  77. */
  78. /*:: declare export var $$iterator: '@@iterator'; */
  79. export var $$iterator = SYMBOL_ITERATOR || '@@iterator'
  80. /**
  81. * Returns true if the provided object implements the Iterator protocol via
  82. * either implementing a `Symbol.iterator` or `"@@iterator"` method.
  83. *
  84. * @example
  85. *
  86. * var isIterable = require('iterall').isIterable
  87. * isIterable([ 1, 2, 3 ]) // true
  88. * isIterable('ABC') // true
  89. * isIterable({ length: 1, 0: 'Alpha' }) // false
  90. * isIterable({ key: 'value' }) // false
  91. * isIterable(new Map()) // true
  92. *
  93. * @param obj
  94. * A value which might implement the Iterable protocol.
  95. * @return {boolean} true if Iterable.
  96. */
  97. /*:: declare export function isIterable(obj: any): boolean; */
  98. export function isIterable(obj) {
  99. return !!getIteratorMethod(obj)
  100. }
  101. /**
  102. * Returns true if the provided object implements the Array-like protocol via
  103. * defining a positive-integer `length` property.
  104. *
  105. * @example
  106. *
  107. * var isArrayLike = require('iterall').isArrayLike
  108. * isArrayLike([ 1, 2, 3 ]) // true
  109. * isArrayLike('ABC') // true
  110. * isArrayLike({ length: 1, 0: 'Alpha' }) // true
  111. * isArrayLike({ key: 'value' }) // false
  112. * isArrayLike(new Map()) // false
  113. *
  114. * @param obj
  115. * A value which might implement the Array-like protocol.
  116. * @return {boolean} true if Array-like.
  117. */
  118. /*:: declare export function isArrayLike(obj: any): boolean; */
  119. export function isArrayLike(obj) {
  120. var length = obj != null && obj.length
  121. return typeof length === 'number' && length >= 0 && length % 1 === 0
  122. }
  123. /**
  124. * Returns true if the provided object is an Object (i.e. not a string literal)
  125. * and is either Iterable or Array-like.
  126. *
  127. * This may be used in place of [Array.isArray()][isArray] to determine if an
  128. * object should be iterated-over. It always excludes string literals and
  129. * includes Arrays (regardless of if it is Iterable). It also includes other
  130. * Array-like objects such as NodeList, TypedArray, and Buffer.
  131. *
  132. * @example
  133. *
  134. * var isCollection = require('iterall').isCollection
  135. * isCollection([ 1, 2, 3 ]) // true
  136. * isCollection('ABC') // false
  137. * isCollection({ length: 1, 0: 'Alpha' }) // true
  138. * isCollection({ key: 'value' }) // false
  139. * isCollection(new Map()) // true
  140. *
  141. * @example
  142. *
  143. * var forEach = require('iterall').forEach
  144. * if (isCollection(obj)) {
  145. * forEach(obj, function (value) {
  146. * console.log(value)
  147. * })
  148. * }
  149. *
  150. * @param obj
  151. * An Object value which might implement the Iterable or Array-like protocols.
  152. * @return {boolean} true if Iterable or Array-like Object.
  153. */
  154. /*:: declare export function isCollection(obj: any): boolean; */
  155. export function isCollection(obj) {
  156. return Object(obj) === obj && (isArrayLike(obj) || isIterable(obj))
  157. }
  158. /**
  159. * If the provided object implements the Iterator protocol, its Iterator object
  160. * is returned. Otherwise returns undefined.
  161. *
  162. * @example
  163. *
  164. * var getIterator = require('iterall').getIterator
  165. * var iterator = getIterator([ 1, 2, 3 ])
  166. * iterator.next() // { value: 1, done: false }
  167. * iterator.next() // { value: 2, done: false }
  168. * iterator.next() // { value: 3, done: false }
  169. * iterator.next() // { value: undefined, done: true }
  170. *
  171. * @template T the type of each iterated value
  172. * @param {Iterable<T>} iterable
  173. * An Iterable object which is the source of an Iterator.
  174. * @return {Iterator<T>} new Iterator instance.
  175. */
  176. /*:: declare export var getIterator:
  177. & (<+TValue>(iterable: Iterable<TValue>) => Iterator<TValue>)
  178. & ((iterable: mixed) => void | Iterator<mixed>); */
  179. export function getIterator(iterable) {
  180. var method = getIteratorMethod(iterable)
  181. if (method) {
  182. return method.call(iterable)
  183. }
  184. }
  185. /**
  186. * If the provided object implements the Iterator protocol, the method
  187. * responsible for producing its Iterator object is returned.
  188. *
  189. * This is used in rare cases for performance tuning. This method must be called
  190. * with obj as the contextual this-argument.
  191. *
  192. * @example
  193. *
  194. * var getIteratorMethod = require('iterall').getIteratorMethod
  195. * var myArray = [ 1, 2, 3 ]
  196. * var method = getIteratorMethod(myArray)
  197. * if (method) {
  198. * var iterator = method.call(myArray)
  199. * }
  200. *
  201. * @template T the type of each iterated value
  202. * @param {Iterable<T>} iterable
  203. * An Iterable object which defines an `@@iterator` method.
  204. * @return {function(): Iterator<T>} `@@iterator` method.
  205. */
  206. /*:: declare export var getIteratorMethod:
  207. & (<+TValue>(iterable: Iterable<TValue>) => (() => Iterator<TValue>))
  208. & ((iterable: mixed) => (void | (() => Iterator<mixed>))); */
  209. export function getIteratorMethod(iterable) {
  210. if (iterable != null) {
  211. var method =
  212. (SYMBOL_ITERATOR && iterable[SYMBOL_ITERATOR]) || iterable['@@iterator']
  213. if (typeof method === 'function') {
  214. return method
  215. }
  216. }
  217. }
  218. /**
  219. * Similar to {@link getIterator}, this method returns a new Iterator given an
  220. * Iterable. However it will also create an Iterator for a non-Iterable
  221. * Array-like collection, such as Array in a non-ES2015 environment.
  222. *
  223. * `createIterator` is complimentary to `forEach`, but allows a "pull"-based
  224. * iteration as opposed to `forEach`'s "push"-based iteration.
  225. *
  226. * `createIterator` produces an Iterator for Array-likes with the same behavior
  227. * as ArrayIteratorPrototype described in the ECMAScript specification, and
  228. * does *not* skip over "holes".
  229. *
  230. * @example
  231. *
  232. * var createIterator = require('iterall').createIterator
  233. *
  234. * var myArraylike = { length: 3, 0: 'Alpha', 1: 'Bravo', 2: 'Charlie' }
  235. * var iterator = createIterator(myArraylike)
  236. * iterator.next() // { value: 'Alpha', done: false }
  237. * iterator.next() // { value: 'Bravo', done: false }
  238. * iterator.next() // { value: 'Charlie', done: false }
  239. * iterator.next() // { value: undefined, done: true }
  240. *
  241. * @template T the type of each iterated value
  242. * @param {Iterable<T>|{ length: number }} collection
  243. * An Iterable or Array-like object to produce an Iterator.
  244. * @return {Iterator<T>} new Iterator instance.
  245. */
  246. /*:: declare export var createIterator:
  247. & (<+TValue>(collection: Iterable<TValue>) => Iterator<TValue>)
  248. & ((collection: {length: number}) => Iterator<mixed>)
  249. & ((collection: mixed) => (void | Iterator<mixed>)); */
  250. export function createIterator(collection) {
  251. if (collection != null) {
  252. var iterator = getIterator(collection)
  253. if (iterator) {
  254. return iterator
  255. }
  256. if (isArrayLike(collection)) {
  257. return new ArrayLikeIterator(collection)
  258. }
  259. }
  260. }
  261. // When the object provided to `createIterator` is not Iterable but is
  262. // Array-like, this simple Iterator is created.
  263. function ArrayLikeIterator(obj) {
  264. this._o = obj
  265. this._i = 0
  266. }
  267. // Note: all Iterators are themselves Iterable.
  268. ArrayLikeIterator.prototype[$$iterator] = function() {
  269. return this
  270. }
  271. // A simple state-machine determines the IteratorResult returned, yielding
  272. // each value in the Array-like object in order of their indicies.
  273. ArrayLikeIterator.prototype.next = function() {
  274. if (this._o === void 0 || this._i >= this._o.length) {
  275. this._o = void 0
  276. return { value: void 0, done: true }
  277. }
  278. return { value: this._o[this._i++], done: false }
  279. }
  280. /**
  281. * Given an object which either implements the Iterable protocol or is
  282. * Array-like, iterate over it, calling the `callback` at each iteration.
  283. *
  284. * Use `forEach` where you would expect to use a `for ... of` loop in ES6.
  285. * However `forEach` adheres to the behavior of [Array#forEach][] described in
  286. * the ECMAScript specification, skipping over "holes" in Array-likes. It will
  287. * also delegate to a `forEach` method on `collection` if one is defined,
  288. * ensuring native performance for `Arrays`.
  289. *
  290. * Similar to [Array#forEach][], the `callback` function accepts three
  291. * arguments, and is provided with `thisArg` as the calling context.
  292. *
  293. * Note: providing an infinite Iterator to forEach will produce an error.
  294. *
  295. * [Array#forEach]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
  296. *
  297. * @example
  298. *
  299. * var forEach = require('iterall').forEach
  300. *
  301. * forEach(myIterable, function (value, index, iterable) {
  302. * console.log(value, index, iterable === myIterable)
  303. * })
  304. *
  305. * @example
  306. *
  307. * // ES6:
  308. * for (let value of myIterable) {
  309. * console.log(value)
  310. * }
  311. *
  312. * // Any JavaScript environment:
  313. * forEach(myIterable, function (value) {
  314. * console.log(value)
  315. * })
  316. *
  317. * @template T the type of each iterated value
  318. * @param {Iterable<T>|{ length: number }} collection
  319. * The Iterable or array to iterate over.
  320. * @param {function(T, number, object)} callback
  321. * Function to execute for each iteration, taking up to three arguments
  322. * @param [thisArg]
  323. * Optional. Value to use as `this` when executing `callback`.
  324. */
  325. /*:: declare export var forEach:
  326. & (<+TValue, TCollection: Iterable<TValue>>(
  327. collection: TCollection,
  328. callbackFn: (value: TValue, index: number, collection: TCollection) => any,
  329. thisArg?: any
  330. ) => void)
  331. & (<TCollection: {length: number}>(
  332. collection: TCollection,
  333. callbackFn: (value: mixed, index: number, collection: TCollection) => any,
  334. thisArg?: any
  335. ) => void); */
  336. export function forEach(collection, callback, thisArg) {
  337. if (collection != null) {
  338. if (typeof collection.forEach === 'function') {
  339. return collection.forEach(callback, thisArg)
  340. }
  341. var i = 0
  342. var iterator = getIterator(collection)
  343. if (iterator) {
  344. var step
  345. while (!(step = iterator.next()).done) {
  346. callback.call(thisArg, step.value, i++, collection)
  347. // Infinite Iterators could cause forEach to run forever.
  348. // After a very large number of iterations, produce an error.
  349. /* istanbul ignore if */
  350. if (i > 9999999) {
  351. throw new TypeError('Near-infinite iteration.')
  352. }
  353. }
  354. } else if (isArrayLike(collection)) {
  355. for (; i < collection.length; i++) {
  356. if (collection.hasOwnProperty(i)) {
  357. callback.call(thisArg, collection[i], i, collection)
  358. }
  359. }
  360. }
  361. }
  362. }
  363. /////////////////////////////////////////////////////
  364. // //
  365. // ASYNC ITERATORS //
  366. // //
  367. /////////////////////////////////////////////////////
  368. /**
  369. * [AsyncIterable](https://tc39.github.io/proposal-async-iteration/#sec-asynciterable-interface)
  370. * is a *protocol* which when implemented allows a JavaScript object to define
  371. * an asynchronous iteration behavior, such as what values are looped over in
  372. * a [`for-await-of`](https://tc39.github.io/proposal-async-iteration/#sec-for-in-and-for-of-statements)
  373. * loop or `iterall`'s {@link forAwaitEach} function.
  374. *
  375. * While described as a proposed addition to the [ES2017 version of JavaScript](https://tc39.github.io/proposal-async-iteration/)
  376. * it can be utilized by any version of JavaScript.
  377. *
  378. * @external AsyncIterable
  379. * @see {@link https://tc39.github.io/proposal-async-iteration/#sec-asynciterable-interface|Async Iteration Proposal}
  380. * @template T The type of each iterated value
  381. * @property {function (): AsyncIterator<T>} Symbol.asyncIterator
  382. * A method which produces an AsyncIterator for this AsyncIterable.
  383. */
  384. /**
  385. * [AsyncIterator](https://tc39.github.io/proposal-async-iteration/#sec-asynciterator-interface)
  386. * is a *protocol* which describes a standard way to produce and consume an
  387. * asynchronous sequence of values, typically the values of the
  388. * {@link AsyncIterable} represented by this {@link AsyncIterator}.
  389. *
  390. * AsyncIterator is similar to Observable or Stream. Like an {@link Iterator} it
  391. * also as a `next()` method, however instead of an IteratorResult,
  392. * calling this method returns a {@link Promise} for a IteratorResult.
  393. *
  394. * While described as a proposed addition to the [ES2017 version of JavaScript](https://tc39.github.io/proposal-async-iteration/)
  395. * it can be utilized by any version of JavaScript.
  396. *
  397. * @external AsyncIterator
  398. * @see {@link https://tc39.github.io/proposal-async-iteration/#sec-asynciterator-interface|Async Iteration Proposal}
  399. */
  400. // In ES2017 (or a polyfilled) environment, this will be Symbol.asyncIterator
  401. var SYMBOL_ASYNC_ITERATOR = SYMBOL && SYMBOL.asyncIterator
  402. /**
  403. * A property name to be used as the name of an AsyncIterable's method
  404. * responsible for producing an Iterator, referred to as `@@asyncIterator`.
  405. * Typically represents the value `Symbol.asyncIterator` but falls back to the
  406. * string `"@@asyncIterator"` when `Symbol.asyncIterator` is not defined.
  407. *
  408. * Use `$$asyncIterator` for defining new AsyncIterables instead of
  409. * `Symbol.asyncIterator`, but do not use it for accessing existing Iterables,
  410. * instead use {@link getAsyncIterator} or {@link isAsyncIterable}.
  411. *
  412. * @example
  413. *
  414. * var $$asyncIterator = require('iterall').$$asyncIterator
  415. *
  416. * function Chirper (to) {
  417. * this.to = to
  418. * }
  419. *
  420. * Chirper.prototype[$$asyncIterator] = function () {
  421. * return {
  422. * to: this.to,
  423. * num: 0,
  424. * next () {
  425. * return new Promise(resolve => {
  426. * if (this.num >= this.to) {
  427. * resolve({ value: undefined, done: true })
  428. * } else {
  429. * setTimeout(() => {
  430. * resolve({ value: this.num++, done: false })
  431. * }, 1000)
  432. * }
  433. * })
  434. * }
  435. * }
  436. * }
  437. *
  438. * var chirper = new Chirper(3)
  439. * for await (var number of chirper) {
  440. * console.log(number) // 0 ...wait... 1 ...wait... 2
  441. * }
  442. *
  443. * @type {Symbol|string}
  444. */
  445. /*:: declare export var $$asyncIterator: '@@asyncIterator'; */
  446. export var $$asyncIterator = SYMBOL_ASYNC_ITERATOR || '@@asyncIterator'
  447. /**
  448. * Returns true if the provided object implements the AsyncIterator protocol via
  449. * either implementing a `Symbol.asyncIterator` or `"@@asyncIterator"` method.
  450. *
  451. * @example
  452. *
  453. * var isAsyncIterable = require('iterall').isAsyncIterable
  454. * isAsyncIterable(myStream) // true
  455. * isAsyncIterable('ABC') // false
  456. *
  457. * @param obj
  458. * A value which might implement the AsyncIterable protocol.
  459. * @return {boolean} true if AsyncIterable.
  460. */
  461. /*:: declare export function isAsyncIterable(obj: any): boolean; */
  462. export function isAsyncIterable(obj) {
  463. return !!getAsyncIteratorMethod(obj)
  464. }
  465. /**
  466. * If the provided object implements the AsyncIterator protocol, its
  467. * AsyncIterator object is returned. Otherwise returns undefined.
  468. *
  469. * @example
  470. *
  471. * var getAsyncIterator = require('iterall').getAsyncIterator
  472. * var asyncIterator = getAsyncIterator(myStream)
  473. * asyncIterator.next().then(console.log) // { value: 1, done: false }
  474. * asyncIterator.next().then(console.log) // { value: 2, done: false }
  475. * asyncIterator.next().then(console.log) // { value: 3, done: false }
  476. * asyncIterator.next().then(console.log) // { value: undefined, done: true }
  477. *
  478. * @template T the type of each iterated value
  479. * @param {AsyncIterable<T>} asyncIterable
  480. * An AsyncIterable object which is the source of an AsyncIterator.
  481. * @return {AsyncIterator<T>} new AsyncIterator instance.
  482. */
  483. /*:: declare export var getAsyncIterator:
  484. & (<+TValue>(asyncIterable: AsyncIterable<TValue>) => AsyncIterator<TValue>)
  485. & ((asyncIterable: mixed) => (void | AsyncIterator<mixed>)); */
  486. export function getAsyncIterator(asyncIterable) {
  487. var method = getAsyncIteratorMethod(asyncIterable)
  488. if (method) {
  489. return method.call(asyncIterable)
  490. }
  491. }
  492. /**
  493. * If the provided object implements the AsyncIterator protocol, the method
  494. * responsible for producing its AsyncIterator object is returned.
  495. *
  496. * This is used in rare cases for performance tuning. This method must be called
  497. * with obj as the contextual this-argument.
  498. *
  499. * @example
  500. *
  501. * var getAsyncIteratorMethod = require('iterall').getAsyncIteratorMethod
  502. * var method = getAsyncIteratorMethod(myStream)
  503. * if (method) {
  504. * var asyncIterator = method.call(myStream)
  505. * }
  506. *
  507. * @template T the type of each iterated value
  508. * @param {AsyncIterable<T>} asyncIterable
  509. * An AsyncIterable object which defines an `@@asyncIterator` method.
  510. * @return {function(): AsyncIterator<T>} `@@asyncIterator` method.
  511. */
  512. /*:: declare export var getAsyncIteratorMethod:
  513. & (<+TValue>(asyncIterable: AsyncIterable<TValue>) => (() => AsyncIterator<TValue>))
  514. & ((asyncIterable: mixed) => (void | (() => AsyncIterator<mixed>))); */
  515. export function getAsyncIteratorMethod(asyncIterable) {
  516. if (asyncIterable != null) {
  517. var method =
  518. (SYMBOL_ASYNC_ITERATOR && asyncIterable[SYMBOL_ASYNC_ITERATOR]) ||
  519. asyncIterable['@@asyncIterator']
  520. if (typeof method === 'function') {
  521. return method
  522. }
  523. }
  524. }
  525. /**
  526. * Similar to {@link getAsyncIterator}, this method returns a new AsyncIterator
  527. * given an AsyncIterable. However it will also create an AsyncIterator for a
  528. * non-async Iterable as well as non-Iterable Array-like collection, such as
  529. * Array in a pre-ES2015 environment.
  530. *
  531. * `createAsyncIterator` is complimentary to `forAwaitEach`, but allows a
  532. * buffering "pull"-based iteration as opposed to `forAwaitEach`'s
  533. * "push"-based iteration.
  534. *
  535. * `createAsyncIterator` produces an AsyncIterator for non-async Iterables as
  536. * described in the ECMAScript proposal [Async-from-Sync Iterator Objects](https://tc39.github.io/proposal-async-iteration/#sec-async-from-sync-iterator-objects).
  537. *
  538. * > Note: Creating `AsyncIterator`s requires the existence of `Promise`.
  539. * > While `Promise` has been available in modern browsers for a number of
  540. * > years, legacy browsers (like IE 11) may require a polyfill.
  541. *
  542. * @example
  543. *
  544. * var createAsyncIterator = require('iterall').createAsyncIterator
  545. *
  546. * var myArraylike = { length: 3, 0: 'Alpha', 1: 'Bravo', 2: 'Charlie' }
  547. * var iterator = createAsyncIterator(myArraylike)
  548. * iterator.next().then(console.log) // { value: 'Alpha', done: false }
  549. * iterator.next().then(console.log) // { value: 'Bravo', done: false }
  550. * iterator.next().then(console.log) // { value: 'Charlie', done: false }
  551. * iterator.next().then(console.log) // { value: undefined, done: true }
  552. *
  553. * @template T the type of each iterated value
  554. * @param {AsyncIterable<T>|Iterable<T>|{ length: number }} source
  555. * An AsyncIterable, Iterable, or Array-like object to produce an Iterator.
  556. * @return {AsyncIterator<T>} new AsyncIterator instance.
  557. */
  558. /*:: declare export var createAsyncIterator:
  559. & (<+TValue>(
  560. collection: Iterable<Promise<TValue> | TValue> | AsyncIterable<TValue>
  561. ) => AsyncIterator<TValue>)
  562. & ((collection: {length: number}) => AsyncIterator<mixed>)
  563. & ((collection: mixed) => (void | AsyncIterator<mixed>)); */
  564. export function createAsyncIterator(source) {
  565. if (source != null) {
  566. var asyncIterator = getAsyncIterator(source)
  567. if (asyncIterator) {
  568. return asyncIterator
  569. }
  570. var iterator = createIterator(source)
  571. if (iterator) {
  572. return new AsyncFromSyncIterator(iterator)
  573. }
  574. }
  575. }
  576. // When the object provided to `createAsyncIterator` is not AsyncIterable but is
  577. // sync Iterable, this simple wrapper is created.
  578. function AsyncFromSyncIterator(iterator) {
  579. this._i = iterator
  580. }
  581. // Note: all AsyncIterators are themselves AsyncIterable.
  582. AsyncFromSyncIterator.prototype[$$asyncIterator] = function() {
  583. return this
  584. }
  585. // A simple state-machine determines the IteratorResult returned, yielding
  586. // each value in the Array-like object in order of their indicies.
  587. AsyncFromSyncIterator.prototype.next = function(value) {
  588. return unwrapAsyncFromSync(this._i, 'next', value)
  589. }
  590. AsyncFromSyncIterator.prototype.return = function(value) {
  591. return this._i.return
  592. ? unwrapAsyncFromSync(this._i, 'return', value)
  593. : Promise.resolve({ value: value, done: true })
  594. }
  595. AsyncFromSyncIterator.prototype.throw = function(value) {
  596. return this._i.throw
  597. ? unwrapAsyncFromSync(this._i, 'throw', value)
  598. : Promise.reject(value)
  599. }
  600. function unwrapAsyncFromSync(iterator, fn, value) {
  601. var step
  602. return new Promise(function(resolve) {
  603. step = iterator[fn](value)
  604. resolve(step.value)
  605. }).then(function(value) {
  606. return { value: value, done: step.done }
  607. })
  608. }
  609. /**
  610. * Given an object which either implements the AsyncIterable protocol or is
  611. * Array-like, iterate over it, calling the `callback` at each iteration.
  612. *
  613. * Use `forAwaitEach` where you would expect to use a [for-await-of](https://tc39.github.io/proposal-async-iteration/#sec-for-in-and-for-of-statements) loop.
  614. *
  615. * Similar to [Array#forEach][], the `callback` function accepts three
  616. * arguments, and is provided with `thisArg` as the calling context.
  617. *
  618. * > Note: Using `forAwaitEach` requires the existence of `Promise`.
  619. * > While `Promise` has been available in modern browsers for a number of
  620. * > years, legacy browsers (like IE 11) may require a polyfill.
  621. *
  622. * @example
  623. *
  624. * var forAwaitEach = require('iterall').forAwaitEach
  625. *
  626. * forAwaitEach(myIterable, function (value, index, iterable) {
  627. * console.log(value, index, iterable === myIterable)
  628. * })
  629. *
  630. * @example
  631. *
  632. * // ES2017:
  633. * for await (let value of myAsyncIterable) {
  634. * console.log(await doSomethingAsync(value))
  635. * }
  636. * console.log('done')
  637. *
  638. * // Any JavaScript environment:
  639. * forAwaitEach(myAsyncIterable, function (value) {
  640. * return doSomethingAsync(value).then(console.log)
  641. * }).then(function () {
  642. * console.log('done')
  643. * })
  644. *
  645. * @template T the type of each iterated value
  646. * @param {AsyncIterable<T>|Iterable<Promise<T> | T>|{ length: number }} source
  647. * The AsyncIterable or array to iterate over.
  648. * @param {function(T, number, object)} callback
  649. * Function to execute for each iteration, taking up to three arguments
  650. * @param [thisArg]
  651. * Optional. Value to use as `this` when executing `callback`.
  652. */
  653. /*:: declare export var forAwaitEach:
  654. & (<+TValue, TCollection: Iterable<Promise<TValue> | TValue> | AsyncIterable<TValue>>(
  655. collection: TCollection,
  656. callbackFn: (value: TValue, index: number, collection: TCollection) => any,
  657. thisArg?: any
  658. ) => Promise<void>)
  659. & (<TCollection: { length: number }>(
  660. collection: TCollection,
  661. callbackFn: (value: mixed, index: number, collection: TCollection) => any,
  662. thisArg?: any
  663. ) => Promise<void>); */
  664. export function forAwaitEach(source, callback, thisArg) {
  665. var asyncIterator = createAsyncIterator(source)
  666. if (asyncIterator) {
  667. var i = 0
  668. return new Promise(function(resolve, reject) {
  669. function next() {
  670. asyncIterator
  671. .next()
  672. .then(function(step) {
  673. if (!step.done) {
  674. Promise.resolve(callback.call(thisArg, step.value, i++, source))
  675. .then(next)
  676. .catch(reject)
  677. } else {
  678. resolve()
  679. }
  680. // Explicitly return null, silencing bluebird-style warnings.
  681. return null
  682. })
  683. .catch(reject)
  684. // Explicitly return null, silencing bluebird-style warnings.
  685. return null
  686. }
  687. next()
  688. })
  689. }
  690. }