IxJS is a set of libraries to compose synchronous and asynchronous collections and Array#extras style composition in JavaScript
The Interactive Extensions for JavaScript (IxJS) brings the Array#extras combinators to iterables, generators, async iterables and async generators. With the introduction of the Symbol.iterator
and generators in ES2015, and subsequent introduction of Symbol.asyncIterator
and async generators, it became obvious we need an abstraction over these data structures for composition, querying and more.
IxJS unifies both synchronous and asynchronous pull-based collections, just as RxJS unified the world of push-based collections. RxJS is great for event-based workflows where the data can be pushed at the rate of the producer, however, IxJS is great at I/O operations where you as the consumer can pull the data when you are ready.
npm install ix
(also read about how we package IxJS below)
Iterable
The Iterable
class a way to create and compose synchronous collections much like Arrays, Maps and Sets in JavaScript using the Array#extras style using the familiar methods you are used to like map
, filter
, reduce
and more.
// ES
import * as Ix from 'ix';
// CommonJS
const Ix = require('ix');
Ix.Iterable.from([1,2,3,4])
.filter(x => x % 2 === 0)
.map(x => x * 2)
.forEach(x => console.log(`Next ${x}`));
// => 4
// => 8
Alternatively, we can use the for ... of
statements to iterate our collections.
// ES
import * as Ix from 'ix';
// CommonJS
const Ix = require('ix');
const results = Ix.Iterable.from([1,2,3,4])
.filter(x => x % 2 === 0)
.map(x => x * 2);
for (let item of results) {
console.log(`Next ${item}`);
}
// => 4
// => 8
Instead of bringing in the entire library for Iterable
, we can pick and choose which operators we want, for bundling concerns.
// ES
import { IterableX as Iterable } from 'ix/iterable';
import 'ix/add/iterable-operators/map';
// CommonJS
const Iterable = require('ix/iterable').IterableX;
require('ix/add/iterable-operators/map');
const results = Iterable.of(1,2,3)
.map(x => x + '!!');
We can also bring in only the operators that we want to using just the operators themselves. Many of these operators take a simple Iterable
source such as an Array
, Map
, Set
or generator function such as our map
and filter
functions.
// ES
import { map } from 'ix/iterable/map';
import { filter } from 'ix/iterable/filter';
// CommonJS
const map = require('ix/iterable/map').map;
const filter = require('ix/iterable/filter').filter;
const source = [1,2,3];
const results = map(
filter(
source,
x => x % 2 === 0
),
x => x * x
);
for (let item of results) {
console.log(`Next: ${item}`);
}
// Next 4
Just like RxJS, IxJS supports "lettable" operators which allow you to chain together operators, keeping the surface area to a minimum on the Iterable
object.
// ES
import { IterableX as Iterable } from 'ix/iterable';
import { map, filter } from 'ix/iterable/pipe';
// CommonJS
const Iterable = require('ix/iterable').IterableX;
const { map, filter } = require('ix/iterable/pipe');
const results = of(1, 2, 3).pipe(
filter(x => x % 2 === 0),
map(x => x * x)
);
for (let item of results) {
console.log(`Next: ${item}`);
}
The Iterable
object implements the iterator pattern in JavaScript by exposing the [Symbol.iterator]
method which in turn exposes the Iterator
class. The iterator yields values by calling the next()
method which returns the IteratorResult
class.
interface Iterable<T> {
[Symbol.iterator](): Iterator<T>;
}
interface Iterator<T> {
next(value?: any): IteratorResult<T>;
return?(value?: any): IteratorResult<T>;
throw?(e?: any): IteratorResult<T>;
}
interface IteratorResult<T> {
value: T;
done: Boolean;
}
AsyncIterable
The AsyncIterable
object is based off the ECMAScript Proposal for Asynchronous Iterators. This would allow us to create asynchronous collections of Promises and be able to use such methods as the map
, filter
, reduce
and other Array#extras methods that you are used to using.
import * as Ix from 'ix';
// CommonJS
const Ix = require('ix');
async function* gen() {
yield 1;
yield 2;
yield 3;
yield 4;
}
Ix.AsyncIterable.from(gen())
.filter(x => x % 2 === 0)
.map(x => x * 2)
.forEach(x => console.log(`Next ${x}`))
.catch(err => console.log(`Error ${err}`));
// => Next 4
// => Next 8
Much like with the Iterable
object where we can iterate through our collections, we can use for await ... of
instead which allows us to iterate over the asynchronous collection.
import * as Ix from 'ix';
// CommonJS
const Ix = require('ix');
async function* gen() {
yield 1;
yield 2;
yield 3;
yield 4;
}
const results = Ix.AsyncIterable.from(gen())
.filter(x => x % 2 === 0)
.map(x => x * 2);
for await (let item of results) {
console.log(`Next ${x}`);
}
// => Next 4
// => Next 8
Instead of bringing in the entire library for AsyncIterable
, we can pick and choose which operators we want, for bundling concerns.
// ES
import { AsyncIterableX as AsyncIterable } from 'ix/asynciterable';
import 'ix/add/asynciterable-operators/map';
// CommonJS
const AsyncIterable = require('ix/asynciterable').AsyncIterableX;
require('ix/add/asynciterable-operators/map');
const results = AsyncIterable.of(1,2,3)
.map(x => x + '!!');
We can also bring in only the operators that we want to using just the operators themselves. Many of these operators take a simple AsyncIterable
source from async function*
functions such as the map
and filter
functions.
// ES
import { map } from 'ix/asynciterable/map';
import { filter } from 'ix/asynciterable/filter';
// CommonJS
const map = require('ix/asynciterable/map').map;
const filter = require('ix/asynciterable/filter').filter;
const source = async function* () {
yield 1;
yield 2;
yield 3;
yield 4;
};
const results = map(
filter(
source(),
x => x % 2 === 0
),
x => x * x
);
for await (let item of results) {
console.log(`Next: ${item}`);
}
Just like RxJS, IxJS supports "lettable" operators which allow you to chain together operators, keeping the surface area to a minimum on the AsyncIterable
object.
// ES
import { AsyncIterableX as AsyncIterable } from 'ix/asynciterable';
import { filter, map } from 'ix/asynciterable/pipe';
// CommonJS
const AsyncIterable = require('ix/asynciterable').AsyncIterableX;
const { filter, map } = require('ix/asynciterable/pipe');
const source = async function* () {
yield 1;
yield 2;
yield 3;
yield 4;
};
const results = from(source()).pipe(
filter(async x => x % 2 === 0),
map(async x => x * x)
);
for await (let item of results) {
console.log(`Next: ${item}`);
}
The AsyncIterable
class implements the async iterator pattern in JavaScript by exposing the [Symbol.asyncIterator]
method which in turn exposes the AsyncIterator
class. The iterator yields values by calling the next()
method which returns a Promise which resolves a IteratorResult
class.
interface AsyncIterable<T> {
[Symbol.asyncIterator](): AsyncIterator<T>;
}
interface AsyncIterator<T> {
[Symbol.asyncIterator](): AsyncIterator<T>;
next(value?: any): Promise<IteratorResult<T>>;
return?(value?: any): Promise<IteratorResult<T>>;
throw?(e?: any): Promise<IteratorResult<T>>;
}
interface IteratorResult<T> {
value: T;
done: Boolean;
}
Using IxJS, you can easily go from an Iterable
to an AsyncIterable
using a number of methods. First, we can use the from
function, either as a standalone or on the Ix.AsyncIterable
object. The from
method accepts a standard Iterable
, Generator
, and Iterator
of Promises, or even another AsyncIterable
.
import { from } from 'ix/asynciterable/from';
import { map } from 'ix/asynciterable/map';
const xs = [1, 2, 3, 4];
const asyncIterable = from(xs);
const mapped = map(asyncIterable, async (item, index) => item * index);
for await (let item of mapped) {
console.log(`Next: ${item}`);
}
In addition, you can use the specialized async methods that are suffixed with Async
, such as mapAsync
, filterAsync
, flatMapAsync
amongst others. These functions accept async functions which allow you to return a Promise
as the result.
import { mapAsync } from 'ix/iterable/mapasync';
const xs = [1, 2, 3, 4];
const mapped = mapAsync(xs, async (item, index) => item * index);
for await (let item of mapped) {
console.log(`Next: ${item}`);
}
We are grateful for contributions to the IxJS project. The IxJS project evolves because of community involvemnent from people such as yourselves. Please read below on how to get involved.
The IxJS project has a strict Code of Conduct that must be adhered at all times. This code of conduct comes from the Contributor Convenant. Please read the full text as to what is and is not permitted.
Read the Contributing Guide on how to get involved with the IxJS project. This includes our development process and how to test your code before committing.
IxJS
is written in TypeScript, but the project is compiled to multiple JS versions and common module formats. The base IxJS package includes all the compilation targets for convenience, but if you're conscientious about your node_modules footprint, don't worry -- we got you. The targets are also published under the @reactivex namespace:
npm install @reactivex/ix-ts # TypeScript target
npm install @reactivex/ix-es5-cjs # ES5 CommonJS target
npm install @reactivex/ix-es5-esm # ES5 ESModules target
npm install @reactivex/ix-es5-umd # ES5 UMD target
npm install @reactivex/ix-es2015-cjs # ES2015 CommonJS target
npm install @reactivex/ix-es2015-esm # ES2015 ESModules target
npm install @reactivex/ix-es2015-umd # ES2015 UMD target
npm install @reactivex/ix-esnext-esm # ESNext CommonJS target
npm install @reactivex/ix-esnext-esm # ESNext ESModules target
npm install @reactivex/ix-esnext-umd # ESNext UMD target
The JS community is a diverse group with a varied list of target environments and tool chains. Publishing multiple packages accommodates projects of all types. Friends targeting the latest JS runtimes can pull in the ESNext + ESM build. Friends needing wide browser support and small download size can use the UMD bundle, which has been run through Google's Closure Compiler with advanced optimizations.
If you think we missed a compilation target and it's a blocker for adoption, please open an issue. We're here for you ❤️.
The MIT License (MIT)
Copyright (c) ReactiveX
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Selector function used to pick a sequence from the given sources.
Dictionary mapping selector values onto resulting sequences.
The source sequence corresponding with the evaluated selector value; otherwise, the default source.
Creates a sequence by concatenating source sequences until a source sequence completes successfully.
The first source.
The rest of the sequence that continues to concatenate source sequences while errors occur.
Creates a sequence by concatenating source sequences until a source sequence completes successfully.
Source sequences.
Sequence that continues to concatenate source sequences while errors occur.
Creates a sequence by concatenating source sequences until a source sequence completes successfully.
Sequence that continues to concatenate source sequences while errors occur.
Sequence that continues to concatenate source sequences while errors occur.
Creates a sequence whose termination or disposal of an iterator causes a finally action to be executed.
Source sequence
Action to run upon termination of the sequence, or when an iterator is disposed.
Source sequence with guarantees on the invocation of the finally action.
Concatenates the iterable sequences obtained by running the result selector for each element in the given source sequence.
Iterable source for which each element will be mapped onto an iterable source that will be concatenated in the result sequence.
The iterable sequence obtained by concatenating the sources returned by result selector for each element in the source.
Computes the average of a sequence of values from the sequence either from the sequence itself or from the selector function.
A sequence of values to calculate the average of.
The average of the sequence of values.
Computes the average of a sequence of values from the sequence either from the sequence itself or from the selector function.
A sequence of values to calculate the average of.
The average of the sequence of values.
Computes the average of a sequence of values from the sequence either from the sequence itself or from the selector function.
A sequence of values to calculate the average of.
The average of the sequence of values.
Computes the average of a sequence of values from the sequence either from the sequence itself or from the selector function.
A sequence of values to calculate the average of.
The average of the sequence of values.
Generates a sequence of buffers over the source sequence, with specified length and possible overlap.
Source sequence
Number of elements for allocated buffers.
Sequence of buffers containing source sequence elements
Creates a sequence that corresponds to the source sequence, concatenating it with the sequence resulting from calling an exception handler function in case of an error.
Source sequence
Handler to invoke when an exception of the specified type occurs.
Source sequence, concatenated with an exception handler result sequence in case of an error.
Returns an async iterable sequence that is the result of invoking the selector on the source sequence, without sharing subscriptions. This operator allows for a fluent style of writing queries that use the same sequence multiple times.
Source sequence that will be shared in the selector function.
Selector function which can use the source sequence as many times as needed, without sharing subscriptions to the source sequence.
An async iterable sequence that contains the elements of a sequence produced by multicasting the source sequence within a selector function.
Concatenates the input sequences.
Source sequences.
Sequence with the elements of the source sequences concatenated.
Returns a number that represents how many elements in the specified sequence satisfy a condition if present, else the number of items in the collection.
A sequence that contains elements to be tested and counted.
Creates an enumerable sequence based on an enumerator factory function.
Sequence that will invoke the iterator factory upon a call to Symbol.iterator.
Returns the elements of the specified sequence or the type parameter's default value in a singleton collection if the sequence is empty.
The sequence to return a default value for if it is empty.
The default value if the sequence is empty.
An that contains the default value if source is empty; otherwise, source.
Creates an enumerable sequence based on an iterable factory function.
Iterable factory function.
Sequence that will invoke the iterable factory upon a call to Symbol.iterator.
Returns elements with a distinct key value by using the specified comparer to compare key values.
Source sequence.
Sequence that contains the elements from the source sequence with distinct key values.
Returns consecutive distinct elements based on a key value by using the specified equality comparer to compare key values.
Source sequence.
Sequence without adjacent non-distinct elements.
Generates an iterable sequence by repeating a source sequence as long as the given loop postcondition holds.
Source sequence to repeat while the condition evaluates true.
Loop condition.
Sequence generated by repeating the given sequence until the condition evaluates to false.
Returns the element at a specified index in a sequence or undefined if the index is out of range.
The source sequence.
The zero-based index of the element to retrieve.
undefined if the index is outside the bounds of the source sequence; otherwise, the element at the specified position in the source sequence.
Returns an empty iterable.
The empty iterable.
Determines whether every element of a sequence satisfy a condition.
Source sequence.
A function to test each element for a condition.
true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false.
Produces the set difference of two sequences by using the an equality comparer to compare values.
A sequence whose elements that are not also in second will be returned.
A sequence whose elements that also occur in the first sequence will cause those elements to be removed from the returned sequence.
A sequence that contains the set difference of the elements of two sequences.
Expands the sequence by recursively applying a selector function.
Source sequence.
Selector function to retrieve the next sequence to expand
Sequence with results from the recursive expansion of the source sequence.
Filters a sequence of values based on a predicate.
Source sequence.
A function to test each source element for a condition.
Sequence that contains elements from the input sequence that satisfy the condition.
Filters a sequence of values based on a predicate.
Source sequence.
A function to test each source element for a condition.
Sequence that contains elements from the input sequence that satisfy the condition.
Returns the value of the first element in the sequence that satisfies the provided testing function. Otherwise undefined is returned.
Source sequence.
Function to execute for every item in the sequence.
The value of the first element in the sequence that satisfies the provided testing function. Otherwise undefined is returned.
Returns the first element in a sequence that satisfies a specified condition if provided, else the first element in the sequence.
Source collection
The first element in the sequence that passes the test in the specified predicate function if provided, else the first element. If there are no elements, undefined is returned.
Projects each element of a sequence to iterable and flattens the resulting sequences into one sequence.
Source sequence
A transform function to apply to each element.
An iterable whose elements are the result of invoking the one-to-many transform function on each element of the input sequence.
Projects each element of a sequence to a potentially async iterable and flattens the resulting sequences into one sequence.
Source sequence
A transform function to apply to each element.
An async iterable whose elements are the result of invoking the one-to-many transform function on each element of the input sequence.
Flattens the source sequence until the specified depth.
Source sequence.
The depth to flatten the source sequence.
The flattened sequence, flattened to the specified depth.
Returns a new sequence that triggers on the second and subsequent triggerings of the input sequence.
Source sequence.
A sequence that triggers on successive pairs of iterations from the input sequence.
Propagates the async sequence that reacts first.
First async sequence.
Second async sequence.
An async sequence that surfaces either of the given sequences, whichever reacted first.
Lazily invokes observer methods for each value in the sequence, and upon successful or exceptional termination.
Source sequence.
Observer to invoke notification calls on.<
Sequence exhibiting the side-effects of observer method invocation upon iteration.
Generated using TypeDoc
Returns a sequence from a dictionary based on the result of evaluating a selector function.