The Interactive Extensions for JavaScript (IxJS)
IxJS, currently stable at version 7.0.0, is a JavaScript library providing the "Interactive Extensions" for composing synchronous and asynchronous pull-based collections. It extends the familiar Array#extras style (like map, filter, reduce) to native JavaScript `Iterable` and `AsyncIterable` objects, as well as generators and async generators. Unlike push-based reactive libraries such as RxJS, IxJS focuses on consumer-driven data flow, making it particularly well-suited for I/O operations where data is pulled when ready. The library offers distinct modules for synchronous (`ix/iterable`) and asynchronous (`ix/asynciterable`) operations, supporting both pipeable operators and a prototype-extension approach for bundling flexibility. It ships with full TypeScript type definitions and maintains a steady release cadence, typically with minor versions for features and bug fixes, with major versions addressing internal changes or deeper refactorings.
Common errors
-
TypeError: (some operator) is not a function
cause Attempting to use an operator (e.g., `map`, `filter`) directly on an `IterableX` or `AsyncIterableX` instance without either importing it as a pipeable operator or adding it via the prototype extension.fixEnsure the operator is imported from `ix/iterable/operators` (or `ix/asynciterable/operators`) and used with the `.pipe()` method, or that the corresponding `ix/add/...` module has been imported for prototype extension. -
TypeError: Cannot read property 'Symbol.asyncIterator' of undefined
cause Attempting to use an asynchronous operator or an `AsyncIterableX` method on a synchronous `Iterable` or a non-async data source.fixVerify that your data source is truly asynchronous (e.g., an `AsyncGenerator`, `Promise` of an `Iterable`) and that you are consistently using imports from `ix/asynciterable` and `ix/asynciterable/operators`. -
SyntaxError: Named export 'from' not found. The requested module 'ix' does not provide an export named 'from'.
cause Incorrect import path for creation functions or operators; these are not directly exposed from the root `ix` package.fixAlways use specific import paths for creators and operators, such as `import { from } from 'ix/iterable';` or `import { map } from 'ix/asynciterable/operators';`.
Warnings
- gotcha IxJS maintains separate modules for synchronous (`ix/iterable`) and asynchronous (`ix/asynciterable`) operations. Mixing operators or creators from these distinct modules on the wrong type of iterable will lead to runtime errors or incorrect behavior.
- gotcha Major version updates (e.g., v5.0.0, v6.0.0, v7.0.0) in IxJS often contain internal refactorings and bug fixes. While explicit breaking API changes might not always be prominently documented, minor behavioral changes or stricter type checks can occur, potentially affecting edge cases.
- deprecated The pattern of directly importing side-effect modules (e.g., `import 'ix/add/iterable-operators/map';`) to extend prototypes is less common in modern applications favoring tree-shaking. While supported, it can lead to larger bundle sizes if not all added operators are used.
Install
-
npm install ix -
yarn add ix -
pnpm add ix
Imports
- from
import { from } from 'ix';import { from } from 'ix/iterable'; - pipeable operators
import { filter, map } from 'ix';import { filter, map } from 'ix/iterable/operators'; - IterableX / AsyncIterableX
const { IterableX } = require('ix/iterable'); // CommonJS is supported, but ESM is preferred in modern applications.import { IterableX } from 'ix/iterable'; import { AsyncIterableX } from 'ix/asynciterable'; - Prototype extensions
import { map } from 'ix/add/iterable-operators/map';import 'ix/add/iterable/of'; import 'ix/add/iterable-operators/map';
Quickstart
import { from } from 'ix/iterable';
import { filter, map } from 'ix/iterable/operators';
// A synchronous generator function
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
yield 6;
}
// Create an Iterable from the generator and apply pipeable operators
const results = from(numberGenerator()).pipe(
filter(x => x % 2 === 0), // Keep only even numbers
map(x => x * 10) // Multiply by 10
);
console.log('Synchronous Iterable results:');
for (const item of results) {
console.log(`Next: ${item}`);
}
// To demonstrate AsyncIterable, imagine an async data source
// import { from as asyncFrom } from 'ix/asynciterable';
// import { filter as asyncFilter, map as asyncMap } from 'ix/asynciterable/operators';
// async function* asyncNumberGenerator() {
// for (let i = 1; i <= 6; i++) {
// await new Promise(resolve => setTimeout(resolve, 50));
// yield i;
// }
// }
// async function runAsyncExample() {
// const asyncResults = asyncFrom(asyncNumberGenerator()).pipe(
// asyncFilter(x => x % 2 !== 0), // Keep only odd numbers
// asyncMap(x => x + 100) // Add 100
// );
// console.log('\nAsynchronous Iterable results:');
// for await (const item of asyncResults) {
// console.log(`Next Async: ${item}`);
// }
// }
// runAsyncExample();