IxJS (Interactive Extensions for JavaScript)

repository·master·Indexed 23 days ago

https://github.com/reactivex/ixjs

IxJS provides libraries for composing synchronous and asynchronous collections using pull-based iterables, bringing Array#extras style combinators to Iterables, AsyncIterables, and Generators. It supports both pipeable operators and a fluent dot-notation API. Version 7.0.0 includes support for the Web Abort API (AbortController and AbortSignal) for cancellation in asynchronous sequences, as well as utilities to convert Observables, DOM streams, Node.js streams, and events into AsyncIterables.

Tokens
8.9K
Snippets
26
Records
43
Agent score
80%

What's inside IxJS

  1. How `AsyncIterable` works in IxJS

    master

    The AsyncIterable object is based on the ECMAScript Asynchronous Iterators proposal. It allows you to create asynchronous collections of Promises and apply operators like map and filter.

    Iteration is performed using the for await ... of statement. You can also use .forEach() and .catch() for handling values and errors in an asynchronous stream.

    // ES
    import { from } from 'ix/asynciterable';
    import { filter, map } from 'ix/asynciterable/operators';
    
    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}`);
    }
  2. Use IxJS with synchronous Iterables

    master

    IxJS provides a standard library of creation factories and operators for synchronous Iterable collections. You can use two programming styles:

    1. Pipeable Operators: Import factories from 'ix/iterable' and operators from 'ix/iterable/operators'. This uses the .pipe() method to chain transformations.
    2. Fluent/Dot-notation: Import the IterableX object from 'ix/iterable' and add specific factories or operators using 'ix/add/iterable/<name>' and 'ix/add/iterable-operators/<name>'. This allows for a more compact, method-chaining style without importing the entire library.

    Iterables can be consumed using for ... of loops or the forEach method.

    import { of } from 'ix/iterable';
    import { map } from 'ix/iterable/operators';
    
    const source = of(1, 2, 3, 4, 5);
    const result = source.pipe(
      map(x => x * x)
    );
    
    for (const item of result) {
      console.log(`Next: ${item}`);
    }
  3. Use IxJS with asynchronous AsyncIterables

    master

    IxJS extends the AsyncIterable concept (ES2018) with a standard library of factories and operators.

    1. Pipeable Operators: Import factories from 'ix/asynciterable' and operators from 'ix/asynciterable/operators'. Use the .pipe() method.
    2. Fluent/Dot-notation: Import AsyncIterableX from 'ix/asynciterable' and add specific modules via 'ix/add/asynciterable/<name>' and 'ix/add/asynciterable-operators/<name>'.

    Async iterables are consumed using for await ... of loops or the forEach method.

    import { as } from 'ix/asynciterable';
    import { map } from 'ix/asynciterable/operators';
    
    const soureFactory = async function*() {
      yield 1;
      yield 2;
      yield 3;
      yield 4;
    };
    
    const source = as(sourceFactory());
    const result = source.pipe(
      map(async x => x * x)
    );
    
    for await (const item of results) {
      console.log(`Next: ${item}`);
    }
  4. How `Iterable` works in IxJS

    master

    The Iterable class allows you to compose synchronous collections using Array#extras style methods like map, filter, and reduce. You can iterate over these collections using a standard for ... of loop or the provided forEach method.

    To keep bundle sizes small, you can import specific operators from ix/iterable/operators and use the .pipe() method, or you can add operators directly to the IterableX prototype using the ix/add modules.

    // Using pipe with specific operators
    import { from } from 'ix/iterable';
    import { filter, map } from 'ix/iterable/operators';
    
    const source = function* () {
      yield 1;
      yield 2;
      yield 3;
      yield 4;
    };
    
    const results = from(source()).pipe(
      filter(x => x % 2 === 0),
      map(x => x * x)
    );
    
    for (let item of results) {
      console.log(`Next: ${item}`);
    }
  5. Handle cancellation in AsyncIterables using AbortSignal

    master

    IxJS supports the Web Abort API (AbortController and AbortSignal) to manage cancellation in asynchronous sequences.

    • Aggregate Operations: Many operators like first or last accept an options object containing a signal.
    • Chain-wide Cancellation: Use the withAbort operator from 'ix/asynciterable/operators' to inject an AbortSignal into an entire operator chain.
    • Fluent Style: You can chain .withAbort(signal) directly in the fluent API.
    import { as, last } from 'ix/asynciterable';
    import { map, withAbort } from 'ix/asynciterable/operators';
    
    const sourceFactory = async function*() {
      yield 1;
      yield 2;
      yield 3;
      yield 4;
    };
    
    const source = as(sourceFactory());
    
    // Passing in an abort Signal to an aggregate
    const controller1 = new AbortController();
    const lastItem = await last(source, { signal: constroller1.signal });
    
    // Add abort signal to a chain
    const controller2 = new AbortController();
    const result = source.pipe(
      withAbort(controller2.signal),
      map(async x => x * x)
    );
    
    for await (const item of result) {
      console.log(`Next: ${item}`);
    }
  6. Use npm scripts for building, cleaning, and testing IxJS

    master

    IxJS provides npm scripts to manage the build lifecycle. You can run these scripts against specific targets (the JavaScript version/environment) and modules (the module format).

    Available Targets (-t or --targets):

    • ix: The main Ix target module bundle.
    • ts: TypeScript.
    • es5, es2015, esnext: Various ECMAScript versions.
    • all: Builds all targets (default).

    Available Modules (-m or --modules):

    • cjs: CommonJS.
    • esm: ECMAScript Modules.
    • umd: Universal Module Definition.
    • all: All module formats (default).

    Scripts:

    • npm run clean: Cleans build targets.
    • npm run build: Cleans and compiles all targets.
    • npm test: Executes tests against built targets.
  7. Use IxJS with fluent dot-notation for Iterables

    master

    To use a fluent API for synchronous iterables, import IterableX and then import only the specific modules needed for the methods you want to use. This keeps your bundle size small.

    • Factories: 'ix/add/iterable/<name>'
    • Operators: 'ix/add/iterable-operators/<name>'
    import { IterableX as Iterable } from 'ix/iterable';
    
    // Add factory and operators
    import 'ix/add/iterable/of';
    import 'ix/add/iterable-operators/map';
    
    const source = Iterable.of(1, 2, 3, 4, 5);
    const result = source.map(x => x);
    
    for (const item of result) {
      console.log(`Next: ${item}`);
    }
  8. Use fluent dot-notation for AsyncIterables

    master

    To use the fluent API for asynchronous iterables, import AsyncIterableX and add specific modules:

    • Factories: 'ix/add/asynciterable/<name>'
    • Operators: 'ix/add/asynciterable-operators/<name>'
    import { AsyncIterableX as AsyncIterable } from 'ix/asynciterable';
    import 'ix/add/asynciterable/as';
    import 'ix/add/asynciterable/last';
    import 'ix/add/asynciterable-operators/map';
    import 'ix/add/asynciterable-operators/withabort';
    
    const sourceFactory = async function*() {
      yield 1;
      yield 2;
      yield 3;
      yield 4;
    };
    
    const controller = new AbortController();
    const result = await as(sourceFactory())
      .withAbort(controller.signal)
      .map(async x => x * x)
      .last({ signal: controller.signal });
  9. Buffer messages by count or time using bufferCountOrTime

    master

    The bufferCountOrTime operator allows you to process items in batches based on either a maximum number of items or a maximum time interval, whichever comes first. This is useful for handling message streams where you want to ensure batches are processed promptly even if the message rate slows down, preventing items from being stuck in a buffer waiting for a specific count to be reached.

    To use it, pipe your subscription into bufferCountOrTime(count, timeMs) and then handle the resulting batches (which are arrays of items).

    await subscription.pipe(
      // emit when buffer hits 16 items, or every 100ms
      bufferCountOrTime(16, 100)
    )
    .forEach(handleBatch)
  10. Use `Iterable` with prototype extensions

    master

    Instead of using .pipe(), you can add operators directly to the IterableX prototype to enable chainable syntax. This is useful for reducing bundle size by only importing what you need.

    // ES
    import { IterableX as Iterable } from 'ix/iterable';
    import 'ix/add/iterable/of';
    import 'ix/add/iterable-operators/map';
    
    const results = Iterable.of(1,2,3)
      .map(x => x + '!!');