iterall
Advanced tools
Comparing version 1.0.3 to 1.1.0
@@ -9,28 +9,83 @@ /** | ||
declare module "iterall" { | ||
// Note: TypeScript already has built-in definitions for | ||
// Iterable, Iterator, AsyncIterable, and AsyncIterator so they are not | ||
// defined here. However you may need to configure TypeScript to include them. | ||
// Note: TypeScript already has built-in definitions for | ||
// Iterable<TValue> and Iterator<TValue> so they are not defined here. | ||
export var $$iterator: symbol | ||
export var $$iterator: symbol | string | ||
export function isIterable(obj: any): obj is Iterable<any> | ||
export function isIterable(obj: any): boolean | ||
export function isArrayLike(obj: any): obj is { length: number } | ||
export function isArrayLike(obj: any): boolean | ||
export function isCollection(obj: any): obj is Iterable<any> | { length: number } | ||
export function isCollection(obj: any): boolean | ||
export function getIterator<TValue>( | ||
iterable: Iterable<TValue> | ||
): Iterator<TValue> | ||
export function getIterator(iterable: any): void | Iterator<any> | ||
export function getIterator<TValue>(iterable: Iterable<TValue>): Iterator<TValue> | ||
export function getIterator(iterable: any): void | Iterator<any> | ||
export function getIteratorMethod<TValue>( | ||
iterable: Iterable<TValue> | ||
): () => Iterator<TValue> | ||
export function getIteratorMethod(iterable: any): void | (() => Iterator<any>) | ||
export function getIteratorMethod<TValue>(iterable: Iterable<TValue>): () => Iterator<TValue> | ||
export function getIteratorMethod(iterable: any): () => Iterator<any> | void | ||
export function createIterator<TValue>( | ||
collection: Iterable<TValue> | ||
): Iterator<TValue> | ||
export function createIterator(collection: { length: number }): Iterator<any> | ||
export function createIterator(collection: any): void | Iterator<any> | ||
export function forEach<TValue, TCollection extends Iterable<TValue>>(collection: TCollection, callbackFn: (value: TValue, index: number, collection: TCollection) => any, thisArg?: any): void | ||
export function forEach<TCollection extends { length: number }>(collection: TCollection, callbackFn: (value: any, index: number, collection: TCollection) => any, thisArg?: any): void | ||
export function forEach<TValue, TCollection extends Iterable<TValue>>( | ||
collection: TCollection, | ||
callbackFn: (value: TValue, index: number, collection: TCollection) => any, | ||
thisArg?: any | ||
): void | ||
export function forEach<TCollection extends { length: number }>( | ||
collection: TCollection, | ||
callbackFn: (value: any, index: number, collection: TCollection) => any, | ||
thisArg?: any | ||
): void | ||
export function createIterator<TValue>(collection: Iterable<TValue>): Iterator<TValue> | ||
export function createIterator(collection: { length: number }): Iterator<any> | ||
export function createIterator(collection: any): void | Iterator<any> | ||
export var $$asyncIterator: symbol | ||
} | ||
export function isAsyncIterable(obj: any): obj is AsyncIterable<any> | ||
export function getAsyncIterator<TValue>( | ||
asyncIterable: AsyncIterable<TValue> | ||
): AsyncIterator<TValue> | ||
export function getAsyncIterator( | ||
asyncIterable: any | ||
): void | AsyncIterator<any> | ||
export function getAsyncIteratorMethod<TValue>( | ||
asyncIterable: AsyncIterable<TValue> | ||
): () => AsyncIterator<TValue> | ||
export function getAsyncIteratorMethod( | ||
asyncIterable: any | ||
): void | (() => AsyncIterator<any>) | ||
export function createAsyncIterator<TValue>( | ||
collection: AsyncIterable<TValue> | Iterable<Promise<TValue> | TValue> | ||
): AsyncIterator<TValue> | ||
export function createAsyncIterator( | ||
collection: {length: number} | ||
): AsyncIterator<any> | ||
export function createAsyncIterator( | ||
collection: any | ||
): void | AsyncIterator<any> | ||
export function forAwaitEach<TValue, TCollection extends AsyncIterable<TValue>>( | ||
collection: TCollection, | ||
callbackFn: (value: TValue, index: number, collection: TCollection) => any, | ||
thisArg?: any | ||
): Promise<void> | ||
export function forAwaitEach<TValue, TCollection extends Iterable<Promise<TValue> | TValue>>( | ||
collection: TCollection, | ||
callbackFn: (value: TValue, index: number, collection: TCollection) => any, | ||
thisArg?: any | ||
): Promise<void> | ||
export function forAwaitEach<TCollection extends { length: number }>( | ||
collection: TCollection, | ||
callbackFn: (value: any, index: number, collection: TCollection) => any, | ||
thisArg?: any | ||
): Promise<void> |
385
index.js
@@ -103,3 +103,3 @@ /** | ||
*/ | ||
function isIterable (obj) { | ||
function isIterable(obj) { | ||
return !!getIteratorMethod(obj) | ||
@@ -126,3 +126,3 @@ } | ||
*/ | ||
function isArrayLike (obj) { | ||
function isArrayLike(obj) { | ||
var length = obj != null && obj.length | ||
@@ -164,3 +164,3 @@ return typeof length === 'number' && length >= 0 && length % 1 === 0 | ||
*/ | ||
function isCollection (obj) { | ||
function isCollection(obj) { | ||
return Object(obj) === obj && (isArrayLike(obj) || isIterable(obj)) | ||
@@ -188,3 +188,3 @@ } | ||
*/ | ||
function getIterator (iterable) { | ||
function getIterator(iterable) { | ||
var method = getIteratorMethod(iterable) | ||
@@ -218,5 +218,6 @@ if (method) { | ||
*/ | ||
function getIteratorMethod (iterable) { | ||
function getIteratorMethod(iterable) { | ||
if (iterable != null) { | ||
var method = SYMBOL_ITERATOR && iterable[SYMBOL_ITERATOR] || iterable['@@iterator'] | ||
var method = | ||
(SYMBOL_ITERATOR && iterable[SYMBOL_ITERATOR]) || iterable['@@iterator'] | ||
if (typeof method === 'function') { | ||
@@ -230,2 +231,65 @@ return method | ||
/** | ||
* Similar to `getIterator()`, this method returns a new Iterator given an | ||
* Iterable. However it will also create an Iterator for a non-Iterable | ||
* Array-like collection, such as Array in a non-ES2015 environment. | ||
* | ||
* `createIterator` is complimentary to `forEach`, but allows a "pull"-based | ||
* iteration as opposed to `forEach`'s "push"-based iteration. | ||
* | ||
* `createIterator` produces an Iterator for Array-likes with the same behavior | ||
* as ArrayIteratorPrototype described in the ECMAScript specification, and | ||
* does *not* skip over "holes". | ||
* | ||
* @example | ||
* | ||
* var createIterator = require('iterall').createIterator | ||
* | ||
* var myArraylike = { length: 3, 0: 'Alpha', 1: 'Bravo', 2: 'Charlie' } | ||
* var iterator = createIterator(myArraylike) | ||
* iterator.next() // { value: 'Alpha', done: false } | ||
* iterator.next() // { value: 'Bravo', done: false } | ||
* iterator.next() // { value: 'Charlie', done: false } | ||
* iterator.next() // { value: undefined, done: true } | ||
* | ||
* @template T the type of each iterated value | ||
* @param {Iterable<T>|{ length: number }} collection | ||
* An Iterable or Array-like object to produce an Iterator. | ||
* @return {Iterator<T>} new Iterator instance. | ||
*/ | ||
function createIterator(collection) { | ||
if (collection != null) { | ||
var iterator = getIterator(collection) | ||
if (iterator) { | ||
return iterator | ||
} | ||
if (isArrayLike(collection)) { | ||
return new ArrayLikeIterator(collection) | ||
} | ||
} | ||
} | ||
exports.createIterator = createIterator | ||
// When the object provided to `createIterator` is not Iterable but is | ||
// Array-like, this simple Iterator is created. | ||
function ArrayLikeIterator(obj) { | ||
this._o = obj | ||
this._i = 0 | ||
} | ||
// Note: all Iterators are themselves Iterable. | ||
ArrayLikeIterator.prototype[$$iterator] = function() { | ||
return this | ||
} | ||
// A simple state-machine determines the IteratorResult returned, yielding | ||
// each value in the Array-like object in order of their indicies. | ||
ArrayLikeIterator.prototype.next = function() { | ||
if (this._o === void 0 || this._i >= this._o.length) { | ||
this._o = void 0 | ||
return { value: void 0, done: true } | ||
} | ||
return { value: this._o[this._i++], done: false } | ||
} | ||
/** | ||
* Given an object which either implements the Iterable protocol or is | ||
@@ -275,3 +339,3 @@ * Array-like, iterate over it, calling the `callback` at each iteration. | ||
*/ | ||
function forEach (collection, callback, thisArg) { | ||
function forEach(collection, callback, thisArg) { | ||
if (collection != null) { | ||
@@ -305,52 +369,224 @@ if (typeof collection.forEach === 'function') { | ||
///////////////////////////////////////////////////// | ||
// // | ||
// ASYNC ITERATORS // | ||
// // | ||
///////////////////////////////////////////////////// | ||
/** | ||
* Similar to `getIterator()`, this method returns a new Iterator given an | ||
* Iterable. However it will also create an Iterator for a non-Iterable | ||
* Array-like collection, such as Array in a non-ES2015 environment. | ||
* [AsyncIterator](https://tc39.github.io/proposal-async-iteration/) | ||
* is a *protocol* which describes a standard way to produce and consume an | ||
* asynchronous sequence of values, typically the values of the AsyncIterable | ||
* represented by this AsyncIterator. | ||
* | ||
* `createIterator` is complimentary to `forEach`, but allows a "pull"-based | ||
* iteration as opposed to `forEach`'s "push"-based iteration. | ||
* AsyncIterator is similar to Observable or Stream. | ||
* | ||
* `createIterator` produces an Iterator for Array-likes with the same behavior | ||
* as ArrayIteratorPrototype described in the ECMAScript specification, and | ||
* does *not* skip over "holes". | ||
* While described as a proposed addition to the [ES2017 version of JavaScript](https://tc39.github.io/proposal-async-iteration/) | ||
* it can be utilized by any version of JavaScript. | ||
* | ||
* @typedef {Object} AsyncIterator | ||
* @template T The type of each iterated value | ||
* @property {function (): Promise<{ value: T, done: boolean }>} next | ||
* A method which produces a Promise which resolves to either the next value | ||
* in a sequence or a result where the `done` property is `true` indicating | ||
* the end of the sequence of values. It may also produce a Promise which | ||
* becomes rejected, indicating a failure. | ||
*/ | ||
/** | ||
* AsyncIterable is a *protocol* which when implemented allows a JavaScript | ||
* object to define their asynchronous iteration behavior, such as what values | ||
* are looped over in a `for-await-of` loop or `iterall`'s `forAwaitEach` | ||
* function. | ||
* | ||
* While described as a proposed addition to the [ES2017 version of JavaScript](https://tc39.github.io/proposal-async-iteration/) | ||
* it can be utilized by any version of JavaScript. | ||
* | ||
* @typedef {Object} AsyncIterable | ||
* @template T The type of each iterated value | ||
* @property {function (): AsyncIterator<T>} Symbol.asyncIterator | ||
* A method which produces an AsyncIterator for this AsyncIterable. | ||
*/ | ||
// In ES2017 (or a polyfilled) environment, this will be Symbol.asyncIterator | ||
var SYMBOL_ASYNC_ITERATOR = typeof Symbol === 'function' && Symbol.asyncIterator | ||
/** | ||
* A property name to be used as the name of an AsyncIterable's method | ||
* responsible for producing an Iterator, referred to as `@@asyncIterator`. | ||
* Typically represents the value `Symbol.asyncIterator` but falls back to the | ||
* string `"@@asyncIterator"` when `Symbol.asyncIterator` is not defined. | ||
* | ||
* Use `$$asyncIterator` for defining new AsyncIterables instead of | ||
* `Symbol.asyncIterator`, but do not use it for accessing existing Iterables, | ||
* instead use `getAsyncIterator()` or `isAsyncIterable()`. | ||
* | ||
* @example | ||
* | ||
* var createIterator = require('iterall').createIterator | ||
* var $$asyncIterator = require('iterall').$$asyncIterator | ||
* | ||
* function Chirper (to) { | ||
* this.to = to | ||
* } | ||
* | ||
* Chirper.prototype[$$asyncIterator] = function () { | ||
* return { | ||
* to: this.to, | ||
* num: 0, | ||
* next () { | ||
* return new Promise(function (resolve) { | ||
* if (this.num >= this.to) { | ||
* resolve({ value: undefined, done: true }) | ||
* } else { | ||
* setTimeout(function () { | ||
* resolve({ value: this.num++, done: false }) | ||
* }, 1000) | ||
* } | ||
* } | ||
* } | ||
* } | ||
* } | ||
* | ||
* var chirper = new Chirper(3) | ||
* for await (var number of chirper) { | ||
* console.log(number) // 0 ...wait... 1 ...wait... 2 | ||
* } | ||
* | ||
* @type {Symbol|string} | ||
*/ | ||
var $$asyncIterator = SYMBOL_ASYNC_ITERATOR || '@@asyncIterator' | ||
exports.$$asyncIterator = $$asyncIterator | ||
/** | ||
* Returns true if the provided object implements the AsyncIterator protocol via | ||
* either implementing a `Symbol.asyncIterator` or `"@@asyncIterator"` method. | ||
* | ||
* @example | ||
* | ||
* var isAsyncIterable = require('iterall').isAsyncIterable | ||
* isAsyncIterable(myStream) // true | ||
* isAsyncIterable('ABC') // false | ||
* | ||
* @param obj | ||
* A value which might implement the AsyncIterable protocol. | ||
* @return {boolean} true if AsyncIterable. | ||
*/ | ||
function isAsyncIterable(obj) { | ||
return !!getAsyncIteratorMethod(obj) | ||
} | ||
exports.isAsyncIterable = isAsyncIterable | ||
/** | ||
* If the provided object implements the AsyncIterator protocol, its | ||
* AsyncIterator object is returned. Otherwise returns undefined. | ||
* | ||
* @example | ||
* | ||
* var getAsyncIterator = require('iterall').getAsyncIterator | ||
* var asyncIterator = getAsyncIterator(myStream) | ||
* asyncIterator.next().then(console.log) // { value: 1, done: false } | ||
* asyncIterator.next().then(console.log) // { value: 2, done: false } | ||
* asyncIterator.next().then(console.log) // { value: 3, done: false } | ||
* asyncIterator.next().then(console.log) // { value: undefined, done: true } | ||
* | ||
* @template T the type of each iterated value | ||
* @param {AsyncIterable<T>} asyncIterable | ||
* An AsyncIterable object which is the source of an AsyncIterator. | ||
* @return {AsyncIterator<T>} new AsyncIterator instance. | ||
*/ | ||
function getAsyncIterator(asyncIterable) { | ||
var method = getAsyncIteratorMethod(asyncIterable) | ||
if (method) { | ||
return method.call(asyncIterable) | ||
} | ||
} | ||
exports.getAsyncIterator = getAsyncIterator | ||
/** | ||
* If the provided object implements the AsyncIterator protocol, the method | ||
* responsible for producing its AsyncIterator object is returned. | ||
* | ||
* This is used in rare cases for performance tuning. This method must be called | ||
* with obj as the contextual this-argument. | ||
* | ||
* @example | ||
* | ||
* var getAsyncIteratorMethod = require('iterall').getAsyncIteratorMethod | ||
* var method = getAsyncIteratorMethod(myStream) | ||
* if (method) { | ||
* var asyncIterator = method.call(myStream) | ||
* } | ||
* | ||
* @template T the type of each iterated value | ||
* @param {AsyncIterable<T>} asyncIterable | ||
* An AsyncIterable object which defines an `@@asyncIterator` method. | ||
* @return {function(): AsyncIterator<T>} `@@asyncIterator` method. | ||
*/ | ||
function getAsyncIteratorMethod(asyncIterable) { | ||
if (asyncIterable != null) { | ||
var method = | ||
(SYMBOL_ASYNC_ITERATOR && asyncIterable[SYMBOL_ASYNC_ITERATOR]) || | ||
asyncIterable['@@asyncIterator'] | ||
if (typeof method === 'function') { | ||
return method | ||
} | ||
} | ||
} | ||
exports.getAsyncIteratorMethod = getAsyncIteratorMethod | ||
/** | ||
* Similar to `getAsyncIterator()`, this method returns a new AsyncIterator | ||
* given an AsyncIterable. However it will also create an AsyncIterator for a | ||
* non-async Iterable as well as non-Iterable Array-like collection, such as | ||
* Array in a pre-ES2015 environment. | ||
* | ||
* `createAsyncIterator` is complimentary to `forAwaitEach`, but allows a | ||
* buffering "pull"-based iteration as opposed to `forAwaitEach`'s | ||
* "push"-based iteration. | ||
* | ||
* `createAsyncIterator` produces an AsyncIterator for non-async Iterables as | ||
* described in the ECMAScript proposal [Async-from-Sync Iterator Objects](https://tc39.github.io/proposal-async-iteration/#sec-async-from-sync-iterator-objects). | ||
* | ||
* > Note: Creating `AsyncIterator`s requires the existence of `Promise`. | ||
* > While `Promise` has been available in modern browsers for a number of | ||
* > years, legacy browsers (like IE 11) may require a polyfill. | ||
* | ||
* @example | ||
* | ||
* var createAsyncIterator = require('iterall').createAsyncIterator | ||
* | ||
* var myArraylike = { length: 3, 0: 'Alpha', 1: 'Bravo', 2: 'Charlie' } | ||
* var iterator = createIterator(myArraylike) | ||
* iterator.next() // { value: 'Alpha', done: false } | ||
* iterator.next() // { value: 'Bravo', done: false } | ||
* iterator.next() // { value: 'Charlie', done: false } | ||
* iterator.next() // { value: undefined, done: true } | ||
* var iterator = createAsyncIterator(myArraylike) | ||
* iterator.next().then(console.log) // { value: 'Alpha', done: false } | ||
* iterator.next().then(console.log) // { value: 'Bravo', done: false } | ||
* iterator.next().then(console.log) // { value: 'Charlie', done: false } | ||
* iterator.next().then(console.log) // { value: undefined, done: true } | ||
* | ||
* @template T the type of each iterated value | ||
* @param {Iterable<T>|{ length: number }} collection | ||
* An Iterable or Array-like object to produce an Iterator. | ||
* @return {Iterator<T>} new Iterator instance. | ||
* @param {AsyncIterable<T>|Iterable<T>|{ length: number }} source | ||
* An AsyncIterable, Iterable, or Array-like object to produce an Iterator. | ||
* @return {AsyncIterator<T>} new AsyncIterator instance. | ||
*/ | ||
function createIterator (collection) { | ||
if (collection != null) { | ||
var iterator = getIterator(collection) | ||
function createAsyncIterator(source) { | ||
if (source != null) { | ||
var asyncIterator = getAsyncIterator(source) | ||
if (asyncIterator) { | ||
return asyncIterator | ||
} | ||
var iterator = createIterator(source) | ||
if (iterator) { | ||
return iterator | ||
return new AsyncFromSyncIterator(iterator) | ||
} | ||
if (isArrayLike(collection)) { | ||
return new ArrayLikeIterator(collection) | ||
} | ||
} | ||
} | ||
exports.createIterator = createIterator | ||
exports.createAsyncIterator = createAsyncIterator | ||
// When the object provided to `createIterator` is not Iterable but is | ||
// Array-like, this simple Iterator is created. | ||
function ArrayLikeIterator (obj) { | ||
this._o = obj | ||
this._i = 0 | ||
// When the object provided to `createAsyncIterator` is not AsyncIterable but is | ||
// sync Iterable, this simple wrapper is created. | ||
function AsyncFromSyncIterator(iterator) { | ||
this._i = iterator | ||
} | ||
// Note: all Iterators are themselves Iterable. | ||
ArrayLikeIterator.prototype[$$iterator] = function () { | ||
// Note: all AsyncIterators are themselves AsyncIterable. | ||
AsyncFromSyncIterator.prototype[$$asyncIterator] = function() { | ||
return this | ||
@@ -361,8 +597,69 @@ } | ||
// each value in the Array-like object in order of their indicies. | ||
ArrayLikeIterator.prototype.next = function () { | ||
if (this._o === void 0 || this._i >= this._o.length) { | ||
this._o = void 0 | ||
return { value: void 0, done: true } | ||
AsyncFromSyncIterator.prototype.next = function() { | ||
var step = this._i.next() | ||
return Promise.resolve(step.value).then(function(value) { | ||
return { value: value, done: step.done } | ||
}) | ||
} | ||
/** | ||
* Given an object which either implements the AsyncIterable protocol or is | ||
* Array-like, iterate over it, calling the `callback` at each iteration. | ||
* | ||
* Use `forAwaitEach` where you would expect to use a `for-await-of` loop. | ||
* | ||
* Similar to [Array#forEach][], the `callback` function accepts three | ||
* arguments, and is provided with `thisArg` as the calling context. | ||
* | ||
* > Note: Using `forAwaitEach` requires the existence of `Promise`. | ||
* > While `Promise` has been available in modern browsers for a number of | ||
* > years, legacy browsers (like IE 11) may require a polyfill. | ||
* | ||
* @example | ||
* | ||
* var forAwaitEach = require('iterall').forAwaitEach | ||
* | ||
* forAwaitEach(myIterable, function (value, index, iterable) { | ||
* console.log(value, index, iterable === myIterable) | ||
* }) | ||
* | ||
* @example | ||
* | ||
* // ES2017: | ||
* for await (let value of myAsyncIterable) { | ||
* console.log(await doSomethingAsync(value)) | ||
* } | ||
* console.log('done') | ||
* | ||
* // Any JavaScript environment: | ||
* forAwaitEach(myAsyncIterable, function (value) { | ||
* return doSomethingAsync(value).then(console.log) | ||
* }).then(function () { | ||
* console.log('done') | ||
* }) | ||
* | ||
* @template T the type of each iterated value | ||
* @param {AsyncIterable<T>|Iterable<Promise<T> | T>|{ length: number }} source | ||
* The AsyncIterable or array to iterate over. | ||
* @param {function(T, number, object)} callback | ||
* Function to execute for each iteration, taking up to three arguments | ||
* @param [thisArg] | ||
* Optional. Value to use as `this` when executing `callback`. | ||
*/ | ||
function forAwaitEach(source, callback, thisArg) { | ||
var asyncIterator = createAsyncIterator(source) | ||
if (asyncIterator) { | ||
var i = 0 | ||
function next() { | ||
return asyncIterator.next().then(function(step) { | ||
if (!step.done) { | ||
return Promise.resolve( | ||
callback.call(thisArg, step.value, i++, source) | ||
).then(next) | ||
} | ||
}) | ||
} | ||
return next() | ||
} | ||
return { value: this._o[this._i++], done: false } | ||
} | ||
exports.forAwaitEach = forAwaitEach |
{ | ||
"name": "iterall", | ||
"version": "1.0.3", | ||
"version": "1.1.0", | ||
"description": "Minimal zero-dependency utilities for using JavaScript Iterables in all environments.", | ||
"main": "index.js", | ||
"typings": "index.d.ts", | ||
"scripts": { | ||
"test": "npm run lint && npm run testonly && npm run testdocs", | ||
"lint": "standard --verbose index.js test.js", | ||
"test": "npm run lint && npm run flow && npm run testonly && npm run testdocs", | ||
"flow": "flow check", | ||
"lint": "npm run prettier -- --write", | ||
"lint-check": "npm run prettier -- --list-different", | ||
"prettier": "prettier --single-quote --no-semi --parser flow '*.{js,flow}'", | ||
"testonly": "nyc --check-coverage --statements 100 node test.js", | ||
"testdocs": "if [ \"$(documentation readme -dgs API | grep -vF 'up to date')\" ]; then echo 'Must run: npm run docs'; exit 1; fi;", | ||
"docs": "documentation readme -gs API", | ||
"travis": "npm run test && nyc report --reporter=text-lcov | coveralls" | ||
"travis": "npm run lint-check && npm run flow && npm run test && nyc report --reporter=text-lcov | coveralls" | ||
}, | ||
@@ -38,7 +42,8 @@ "repository": { | ||
"devDependencies": { | ||
"coveralls": "2.11.9", | ||
"coveralls": "2.13.0", | ||
"documentation": "4.0.0-beta5", | ||
"nyc": "6.6.1", | ||
"standard": "7.1.2" | ||
"flow-bin": "0.44.2", | ||
"nyc": "10.2.0", | ||
"prettier": "1.2.2" | ||
} | ||
} |
292
README.md
@@ -1,9 +0,10 @@ | ||
# JavaScript [Iterators][] for all! | ||
# JavaScript [Iterators][] and [AsyncIterators][] for all! | ||
[![Build Status](https://travis-ci.org/leebyron/iterall.svg?branch=master)](https://travis-ci.org/leebyron/iterall) [![Coverage Status](https://coveralls.io/repos/github/leebyron/iterall/badge.svg?branch=master)](https://coveralls.io/github/leebyron/iterall?branch=master) ![568 bytes minified and gzipped](https://img.shields.io/badge/min%20gzip%20size-606%20B-blue.svg) | ||
[![Build Status](https://travis-ci.org/leebyron/iterall.svg?branch=master)](https://travis-ci.org/leebyron/iterall) [![Coverage Status](https://coveralls.io/repos/github/leebyron/iterall/badge.svg?branch=master)](https://coveralls.io/github/leebyron/iterall?branch=master) ![710 bytes minified and gzipped](https://img.shields.io/badge/min%20gzip%20size-710%20B-blue.svg) | ||
`iterall` provides a few crucial utilities for implementing and working with | ||
[Iterables][iterators] and [Array-likes][array-like] in all JavaScript | ||
environments, even old versions of Internet Explorer, in a tiny library weighing | ||
well under 1KB when minified and gzipped. | ||
[Iterables][iterators], [Async Iterables][asynciterators] and | ||
[Array-likes][array-like] in all JavaScript environments, even old versions of | ||
Internet Explorer, in a tiny library weighing well under 1KB when minified | ||
and gzipped. | ||
@@ -35,2 +36,11 @@ This is a library for libraries. If your library takes Arrays as input, accept | ||
} | ||
// Accepts all AsyncIterators, in any JavaScript environment! ⏳ | ||
var forAwaitEach = require('iterall').forAwaitEach | ||
forAwaitEach(thing, function (item, i) { | ||
console.log('Index: ' + i, item) | ||
}).then(function () { | ||
console.log('Done') | ||
}) | ||
``` | ||
@@ -96,2 +106,14 @@ | ||
## Why use AsyncIterators? | ||
In the same way that `Iterator` provides a common interface for accessing many | ||
different kinds of data-structures, `AsyncIterator` provides a common interface | ||
over a stream (or Observable) of values. | ||
Async Iterators are not yet an official part of JavaScript, however they're | ||
a "Stage 3" proposal to be added, and browser vendors are | ||
[working on adding support](https://bugs.chromium.org/p/v8/issues/detail?id=5855). | ||
However, Async Iterators can be both safely defined and used today by | ||
_any version of JavaScript_, by using the utilities in `iterall`. | ||
## FAQ | ||
@@ -362,2 +384,34 @@ | ||
### createIterator | ||
Similar to `getIterator()`, this method returns a new Iterator given an | ||
Iterable. However it will also create an Iterator for a non-Iterable | ||
Array-like collection, such as Array in a non-ES2015 environment. | ||
`createIterator` is complimentary to `forEach`, but allows a "pull"-based | ||
iteration as opposed to `forEach`'s "push"-based iteration. | ||
`createIterator` produces an Iterator for Array-likes with the same behavior | ||
as ArrayIteratorPrototype described in the ECMAScript specification, and | ||
does _not_ skip over "holes". | ||
**Parameters** | ||
- `collection` **(Iterable<T> | {length: [number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)})** An Iterable or Array-like object to produce an Iterator. | ||
**Examples** | ||
```javascript | ||
var createIterator = require('iterall').createIterator | ||
var myArraylike = { length: 3, 0: 'Alpha', 1: 'Bravo', 2: 'Charlie' } | ||
var iterator = createIterator(myArraylike) | ||
iterator.next() // { value: 'Alpha', done: false } | ||
iterator.next() // { value: 'Bravo', done: false } | ||
iterator.next() // { value: 'Charlie', done: false } | ||
iterator.next() // { value: undefined, done: true } | ||
``` | ||
Returns **Iterator<T>** new Iterator instance. | ||
### forEach | ||
@@ -409,18 +463,87 @@ | ||
### createIterator | ||
### AsyncIterator | ||
Similar to `getIterator()`, this method returns a new Iterator given an | ||
Iterable. However it will also create an Iterator for a non-Iterable | ||
Array-like collection, such as Array in a non-ES2015 environment. | ||
[AsyncIterator](https://tc39.github.io/proposal-async-iteration/) | ||
is a _protocol_ which describes a standard way to produce and consume an | ||
asynchronous sequence of values, typically the values of the AsyncIterable | ||
represented by this AsyncIterator. | ||
`createIterator` is complimentary to `forEach`, but allows a "pull"-based | ||
iteration as opposed to `forEach`'s "push"-based iteration. | ||
AsyncIterator is similar to Observable or Stream. | ||
`createIterator` produces an Iterator for Array-likes with the same behavior | ||
as ArrayIteratorPrototype described in the ECMAScript specification, and | ||
does _not_ skip over "holes". | ||
While described as a proposed addition to the [ES2017 version of JavaScript](https://tc39.github.io/proposal-async-iteration/) | ||
it can be utilized by any version of JavaScript. | ||
**Properties** | ||
- `next` **function (): [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<{value: T, done: [boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)}>** A method which produces a Promise which resolves to either the next value | ||
in a sequence or a result where the `done` property is `true` indicating | ||
the end of the sequence of values. It may also produce a Promise which | ||
becomes rejected, indicating a failure. | ||
### AsyncIterable | ||
AsyncIterable is a _protocol_ which when implemented allows a JavaScript | ||
object to define their asynchronous iteration behavior, such as what values | ||
are looped over in a `for-await-of` loop or `iterall`'s `forAwaitEach` | ||
function. | ||
While described as a proposed addition to the [ES2017 version of JavaScript](https://tc39.github.io/proposal-async-iteration/) | ||
it can be utilized by any version of JavaScript. | ||
**Properties** | ||
- `Symbol.asyncIterator` **function (): AsyncIterator<T>** A method which produces an AsyncIterator for this AsyncIterable. | ||
### $$asyncIterator | ||
A property name to be used as the name of an AsyncIterable's method | ||
responsible for producing an Iterator, referred to as `@@asyncIterator`. | ||
Typically represents the value `Symbol.asyncIterator` but falls back to the | ||
string `"@@asyncIterator"` when `Symbol.asyncIterator` is not defined. | ||
Use `$$asyncIterator` for defining new AsyncIterables instead of | ||
`Symbol.asyncIterator`, but do not use it for accessing existing Iterables, | ||
instead use `getAsyncIterator()` or `isAsyncIterable()`. | ||
**Examples** | ||
```javascript | ||
var $$asyncIterator = require('iterall').$$asyncIterator | ||
function Chirper (to) { | ||
this.to = to | ||
} | ||
Chirper.prototype[$$asyncIterator] = function () { | ||
return { | ||
to: this.to, | ||
num: 0, | ||
next () { | ||
return new Promise(function (resolve) { | ||
if (this.num >= this.to) { | ||
resolve({ value: undefined, done: true }) | ||
} else { | ||
setTimeout(function () { | ||
resolve({ value: this.num++, done: false }) | ||
}, 1000) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
var chirper = new Chirper(3) | ||
for await (var number of chirper) { | ||
console.log(number) // 0 ...wait... 1 ...wait... 2 | ||
} | ||
``` | ||
### isAsyncIterable | ||
Returns true if the provided object implements the AsyncIterator protocol via | ||
either implementing a `Symbol.asyncIterator` or `"@@asyncIterator"` method. | ||
**Parameters** | ||
- `collection` **(Iterable<T> | {length: [number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)})** An Iterable or Array-like object to produce an Iterator. | ||
- `obj` A value which might implement the AsyncIterable protocol. | ||
@@ -430,14 +553,137 @@ **Examples** | ||
```javascript | ||
var createIterator = require('iterall').createIterator | ||
var isAsyncIterable = require('iterall').isAsyncIterable | ||
isAsyncIterable(myStream) // true | ||
isAsyncIterable('ABC') // false | ||
``` | ||
Returns **[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** true if AsyncIterable. | ||
### getAsyncIterator | ||
If the provided object implements the AsyncIterator protocol, its | ||
AsyncIterator object is returned. Otherwise returns undefined. | ||
**Parameters** | ||
- `asyncIterable` **AsyncIterable<T>** An AsyncIterable object which is the source of an AsyncIterator. | ||
**Examples** | ||
```javascript | ||
var getAsyncIterator = require('iterall').getAsyncIterator | ||
var asyncIterator = getAsyncIterator(myStream) | ||
asyncIterator.next().then(console.log) // { value: 1, done: false } | ||
asyncIterator.next().then(console.log) // { value: 2, done: false } | ||
asyncIterator.next().then(console.log) // { value: 3, done: false } | ||
asyncIterator.next().then(console.log) // { value: undefined, done: true } | ||
``` | ||
Returns **AsyncIterator<T>** new AsyncIterator instance. | ||
### getAsyncIteratorMethod | ||
If the provided object implements the AsyncIterator protocol, the method | ||
responsible for producing its AsyncIterator object is returned. | ||
This is used in rare cases for performance tuning. This method must be called | ||
with obj as the contextual this-argument. | ||
**Parameters** | ||
- `asyncIterable` **AsyncIterable<T>** An AsyncIterable object which defines an `@@asyncIterator` method. | ||
**Examples** | ||
```javascript | ||
var getAsyncIteratorMethod = require('iterall').getAsyncIteratorMethod | ||
var method = getAsyncIteratorMethod(myStream) | ||
if (method) { | ||
var asyncIterator = method.call(myStream) | ||
} | ||
``` | ||
Returns **function (): AsyncIterator<T>** `@@asyncIterator` method. | ||
### createAsyncIterator | ||
Similar to `getAsyncIterator()`, this method returns a new AsyncIterator | ||
given an AsyncIterable. However it will also create an AsyncIterator for a | ||
non-async Iterable as well as non-Iterable Array-like collection, such as | ||
Array in a pre-ES2015 environment. | ||
`createAsyncIterator` is complimentary to `forAwaitEach`, but allows a | ||
buffering "pull"-based iteration as opposed to `forAwaitEach`'s | ||
"push"-based iteration. | ||
`createAsyncIterator` produces an AsyncIterator for non-async Iterables as | ||
described in the ECMAScript proposal [Async-from-Sync Iterator Objects](https://tc39.github.io/proposal-async-iteration/#sec-async-from-sync-iterator-objects). | ||
> Note: Creating `AsyncIterator`s requires the existence of `Promise`. | ||
> While `Promise` has been available in modern browsers for a number of | ||
> years, legacy browsers (like IE 11) may require a polyfill. | ||
**Parameters** | ||
- `source` **(AsyncIterable<T> | Iterable<T> | {length: [number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)})** An AsyncIterable, Iterable, or Array-like object to produce an Iterator. | ||
**Examples** | ||
```javascript | ||
var createAsyncIterator = require('iterall').createAsyncIterator | ||
var myArraylike = { length: 3, 0: 'Alpha', 1: 'Bravo', 2: 'Charlie' } | ||
var iterator = createIterator(myArraylike) | ||
iterator.next() // { value: 'Alpha', done: false } | ||
iterator.next() // { value: 'Bravo', done: false } | ||
iterator.next() // { value: 'Charlie', done: false } | ||
iterator.next() // { value: undefined, done: true } | ||
var iterator = createAsyncIterator(myArraylike) | ||
iterator.next().then(console.log) // { value: 'Alpha', done: false } | ||
iterator.next().then(console.log) // { value: 'Bravo', done: false } | ||
iterator.next().then(console.log) // { value: 'Charlie', done: false } | ||
iterator.next().then(console.log) // { value: undefined, done: true } | ||
``` | ||
Returns **Iterator<T>** new Iterator instance. | ||
Returns **AsyncIterator<T>** new AsyncIterator instance. | ||
### forAwaitEach | ||
Given an object which either implements the AsyncIterable protocol or is | ||
Array-like, iterate over it, calling the `callback` at each iteration. | ||
Use `forAwaitEach` where you would expect to use a `for-await-of` loop. | ||
Similar to [Array#forEach][], the `callback` function accepts three | ||
arguments, and is provided with `thisArg` as the calling context. | ||
> Note: Using `forAwaitEach` requires the existence of `Promise`. | ||
> While `Promise` has been available in modern browsers for a number of | ||
> years, legacy browsers (like IE 11) may require a polyfill. | ||
**Parameters** | ||
- `source` **(AsyncIterable<T> | Iterable<([Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<T> | T)> | {length: [number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)})** The AsyncIterable or array to iterate over. | ||
- `callback` **function (T, [number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number), [object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object))** Function to execute for each iteration, taking up to three arguments | ||
- `thisArg` Optional. Value to use as `this` when executing `callback`. | ||
**Examples** | ||
```javascript | ||
var forAwaitEach = require('iterall').forAwaitEach | ||
forAwaitEach(myIterable, function (value, index, iterable) { | ||
console.log(value, index, iterable === myIterable) | ||
}) | ||
``` | ||
```javascript | ||
// ES2017: | ||
for await (let value of myAsyncIterable) { | ||
console.log(await doSomethingAsync(value)) | ||
} | ||
console.log('done') | ||
// Any JavaScript environment: | ||
forAwaitEach(myAsyncIterable, function (value) { | ||
return doSomethingAsync(value).then(console.log) | ||
}).then(function () { | ||
console.log('done') | ||
}) | ||
``` | ||
## Contributing | ||
@@ -478,2 +724,4 @@ | ||
[asynciterators]: https://tc39.github.io/proposal-async-iteration/ | ||
[linked list]: https://en.wikipedia.org/wiki/Linked_list | ||
@@ -480,0 +728,0 @@ |
Sorry, the diff of this file is not supported yet
59777
704
742
5