Class AsyncArrayStream<Input, Handler>

A class that contains an asynchronous iterator and a set of operations to imitate Rust iterators. Operations (map, filter, etc.) are not executed until the iterator is consumed. It can be used with any async or syncronous iterable. It can also work with a function that returns a promise that resolves to an item or null (to signal that the iterator is exhausted).

An example of how this might be used with a fetch request with the promise generator.

let pageToken: string | null = null;
async function generatePromise(): Promise<Item[] | null> {
const response = await fetch("my-external-api", { page_size: 50, page_token: pageToken }) as { ok: boolean, items: Item[], page_token: string };
if (response.ok) {
pageToken = response.page_token;
return response.items;
}
return null;
}

let items: Item[] = [];
const stream = new AsyncArrayStream({ promise: generatePromise });

document.querySelector(".load-more").addEventListener("click", async () => {
const next = await stream.read().next();
if (!next.done && next.value) {
items = items.concat(next.value);
renderList(items)
}
})

Type Parameters

Constructors

Properties

drop: ((n: number) => AsyncArrayStream<Input, Handler>) = ...

Alias for skip, which will discard the first n items from the iterator, c.f. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator/drop

Type declaration

    • (n): AsyncArrayStream<Input, Handler>
    • Return a new iterator that will discard the first n items from the iterator, i.e.

      const stream = await new AsyncArrayStream([0, 1, 2, 3, 4]).skip(2).collect();
      // stream = [2, 3, 4]

      Parameters

      • n: number

      Returns AsyncArrayStream<Input, Handler>

every: ((fn: MaybeAsyncFn<Input, boolean>) => Promise<HandlerReturnType<Handler, Input, boolean>>) = ...

Alias for all, which will consume the iterator and return whether the predicate matches all items, c.f. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator/every

Type declaration

    • (fn): Promise<HandlerReturnType<Handler, Input, boolean>>
    • Consume the iterator and return a boolean if all the items cause the function to return true. It is short circuiting and will return after any item returns false, i.e.

      const allEven = await new AsyncArrayStream([2, 4, 6, 8, 10])
      .all((item) => item % 2 === 0);
      console.log(allEven) // true

      or

      const allEven = await new AsyncArrayStream([2, 4, 6, 8, 9])
      .all((item) => item % 2 === 0);
      console.log(allEven) // false

      Parameters

      • fn: MaybeAsyncFn<Input, boolean>

      Returns Promise<HandlerReturnType<Handler, Input, boolean>>

some: ((fn: MaybeAsyncFn<Input, boolean>) => Promise<HandlerReturnType<Handler, Input, boolean>>) = ...

Type declaration

    • (fn): Promise<HandlerReturnType<Handler, Input, boolean>>
    • Consume the iterator and return a boolean if any item causes the function to return true. It is short circuiting and will return after any item returns true, i.e.

      const hasEven = await new AsyncArrayStream([1, 2, 3, 4, 5])
      .any((item) => item % 2 === 0);
      console.log(hasEven) // true

      or

      const hasEven = await new AsyncArrayStream([1, 3, 5])
      .any((item) => item % 2 === 0);
      console.log(hasEven) // false

      Parameters

      • fn: MaybeAsyncFn<Input, boolean>

      Returns Promise<HandlerReturnType<Handler, Input, boolean>>

toArray: (() => Promise<HandlerReturnType<Handler, Input, Input[]>>) = ...

Alias for collect, which will consume the iterator and return the items as an array, c.f. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator/toArray

Type declaration

    • (): Promise<HandlerReturnType<Handler, Input, Input[]>>
    • Consume the iterator and return the items in an array. It is identical in functionality to a reduce method with an array that pushes the items, i.e.

      const stream = await new AsyncArrayStream([1, 2, 3, 4, 5]).collect();
      // stream = [1, 2, 3, 4, 5]

      const stream2 = await new AsyncArrayStream([1,2,3,4,5]).reduce((acc, next) => {
      acc.push(next);
      return acc;
      }, []);
      // stream2 = [1, 2, 3, 4, 5]

      Returns Promise<HandlerReturnType<Handler, Input, Input[]>>

Methods

  • Consume the iterator and return a boolean if all the items cause the function to return true. It is short circuiting and will return after any item returns false, i.e.

    const allEven = await new AsyncArrayStream([2, 4, 6, 8, 10])
    .all((item) => item % 2 === 0);
    console.log(allEven) // true

    or

    const allEven = await new AsyncArrayStream([2, 4, 6, 8, 9])
    .all((item) => item % 2 === 0);
    console.log(allEven) // false

    Parameters

    • fn: MaybeAsyncFn<Input, boolean>

    Returns Promise<HandlerReturnType<Handler, Input, boolean>>

  • Consume the iterator and return a boolean if any item causes the function to return true. It is short circuiting and will return after any item returns true, i.e.

    const hasEven = await new AsyncArrayStream([1, 2, 3, 4, 5])
    .any((item) => item % 2 === 0);
    console.log(hasEven) // true

    or

    const hasEven = await new AsyncArrayStream([1, 3, 5])
    .any((item) => item % 2 === 0);
    console.log(hasEven) // false

    Parameters

    • fn: MaybeAsyncFn<Input, boolean>

    Returns Promise<HandlerReturnType<Handler, Input, boolean>>

  • Consume the iterator and return the items in an array. It is identical in functionality to a reduce method with an array that pushes the items, i.e.

    const stream = await new AsyncArrayStream([1, 2, 3, 4, 5]).collect();
    // stream = [1, 2, 3, 4, 5]

    const stream2 = await new AsyncArrayStream([1,2,3,4,5]).reduce((acc, next) => {
    acc.push(next);
    return acc;
    }, []);
    // stream2 = [1, 2, 3, 4, 5]

    Returns Promise<HandlerReturnType<Handler, Input, Input[]>>

  • Consume the iterator and return however many items it contains, i.e.

    const stream = await new AsyncArrayStream([100, 200, 300).count();
    // stream = 3

    Returns Promise<HandlerReturnType<Handler, Input, number>>

  • Removes duplicate items from the stream based on a provided callback function. If no callback is provided, a shallow comparison is used, e.g..

    const stream = await new AsyncArrayStream([1, 2, 3, 4, 5, 1, 2, 3, 4, 5])
    .dedupe()
    .collect();
    console.log(stream); // [1, 2, 3, 4, 5]

    Type Parameters

    • T

    Parameters

    • cb: ((item: Input) => T | Promise<T>) = ...
        • (item): T | Promise<T>
        • Parameters

          Returns T | Promise<T>

    Returns AsyncArrayStream<Input, Handler>

  • Add a filter operation to the iterator that will be resolved when the iterator is finalized. A filter operation should be pure and return a boolean (or Promise) corresponding to whether the item should be retained or filtered out, i.e.

    const stream = await new AsyncArrayStream([1, 2, 3, 4, 5])
    .filter((item) => item % 2 === 0)
    .collect();
    console.log(stream); // [2, 4]

    Parameters

    • fn: MaybeAsyncFn<Input, boolean>

    Returns AsyncArrayStream<Input, Handler>

  • Add a filterMap operation to the iterator that will be resolved when the iterator is finalized. A filterMap operation takes an item of type A and returns B | null | undefined | false (or a Promise that resolves to any of these values). Any type except B will cause the item to be filtered out. A filterMap functions is typically pure, i.e.

    const stream = await new AsyncArrayStream([0, 1, 2, 3, 4])
    .filterMap((item) => item % 2 === 0 ? item ** 2 : null)
    .collect();
    // stream = [0, 4, 16]

    NOTE: Map functions change the type of the iterator, but if you call the function without reassigning the variable or chaining methods, then the type will be incorrect, i.e.

    const stream = new ArrayStream([1, 2, 3, 4, 5])
    stream.map(item => String.fromCharCode(item + 65));

    then the type of stream will be ArrayStream<number, Breaker<number>> instead of ArrayStream<string, Breaker<string>>. Instead, do one of these two:

    const stream = new ArrayStream([1, 2, 3, 4, 5])
    .map(item => String.fromCharCode(item + 65));

    or

    let stream = new ArrayStream([1, 2, 3, 4, 5]);
    stream = stream.map(item => String.fromCharCode(item + 65));

    Type Parameters

    • End

    Parameters

    • fn: MaybeAsyncFn<Input,
          | undefined
          | null
          | false
          | End>

    Returns AsyncArrayStream<End, NarrowHandlerType<Handler, Input, End>>

  • Consume the iterator and return the first item that causes the function to return true. If the item is not found, it will return null. This method can be used on an infinite generator but will never return if the item is never found. The function short circuits on the first item that returns true and will not consume the rest of the iterator, i.e.

    const item = await new AsyncArrayStream([1, 2, 3, 4, 5])
    .find((item) => item % 2 === 0);
    console.log(item); // 2

    or

    const item = await new AsyncArrayStream([1, 3, 5, 7, 9])
    .find((item) => item % 2 === 0);
    console.log(item); // null

    Parameters

    • fn: MaybeAsyncFn<Input, boolean>

    Returns Promise<HandlerReturnType<Handler, Input, null | Input>>

  • Consume the iterator and return the index of first item that causes the function to return true. If the item is not found, it will return -1. This method can be used on an infinite generator but will never return if the item is never found. The function short circuits on the first item that returns true and will not consume the rest of the iterator, i.e.

    const position = await new AsyncArrayStream([1, 2, 3, 4, 5])
    .findIndex((item) => item % 2 === 0);
    console.log(position); // 1

    or

    const position = await new AsyncArrayStream([1, 3, 5, 7, 9])
    .findIndex((item) => item % 2 === 0);
    console.log(position); // -1

    Parameters

    • fn: MaybeAsyncFn<Input, boolean>

    Returns Promise<HandlerReturnType<Handler, Input, number>>

  • Consume the iterator and return the first item that causes the function to return true starting from the last item in the iteraotr. If the item is not found, it will return null. This method cannot be used on an infinite generator because it needs to consume the entire iterator to start from the end. The function does not short circuit and will consume the entire iterator, i.e.

    const null = await new AsyncArrayStream([1, 2, 3, 4, 5])
    .findLast((item) => item % 2 === 0);
    console.log(null); // 4

    or

    const item = await new AsyncArrayStream([1, 3, 5, 7, 9])
    .findLast((item) => item % 2 === 0);
    console.log(item); // null

    Parameters

    • fn: MaybeAsyncFn<Input, boolean>

    Returns Promise<HandlerReturnType<Handler, Input, null | Input>>

  • Consume the iterator and return the index of the first item that causes the function to return true starting from the last item in the iteraotr. If the item is not found, it will return null. This method cannot be used on an infinite generator because it needs to consume the entire iterator to start from the end. The function does not short circuit and will consume the entire iterator, i.e.

    const position = await new AsyncArrayStream([1, 2, 3, 4, 5])
    .findLastIndex((item) => item % 2 === 0);
    console.log(position); // 4

    or

    const position = await new AsyncArrayStream([1, 3, 5, 7, 9])
    .findLastIndex((item) => item % 2 === 0);
    console.log(position); // -1

    Parameters

    • fn: MaybeAsyncFn<Input, boolean>

    Returns Promise<HandlerReturnType<Handler, Input, number>>

  • Consume the iterator and collect all items into an array flattened to the specified depth, i.e.

    const stream = await new AsyncArrayStream([[1, 2], [3, [4, 5]], [5, [6, [7, 8]]]]).flat(2);
    // stream = [1, 2, 3, 4, 5, 5, 6, [7, 8]]

    Type Parameters

    • D extends number = 1

    Parameters

    • Optionald: D

    Returns Promise<HandlerReturnType<Handler, Input, FlatArray<Input, D>[]>>

  • Add a forEach operation to the iterator that will be resolved when the iterator is finalized. A forEach operation takes an iterator of type A and returns void or Promise. A forEach function should be impure and cause side effects.

    let sum = 0;
    const sum2 = await new AsyncArrayStream([1, 2, 3, 4, 5])
    .forEach((item) => sum += item)
    .reduce((acc, next) => acc + next, 0);
    console.log(sum === sum2); // true

    Parameters

    • fn: MaybeAsyncFn<Input, unknown>

    Returns AsyncArrayStream<Input, Handler>

  • Consume the iterator and return if any item is equal to the input. This is short circuiting and will return after any item is equal to the input. NOTE: This will not work correctly for reference values, i.e.

    const hasTwo = await new AsyncArrayStream([1, 2, 3, 4, 5])
    .includes(2);
    console.log(hasTwo); // true

    but this will not work:

    const obj = { a: 1 };
    const hasObj = await new AsyncArrayStream([{ a: 1 }, { a: 2 }])
    .includes(obj);
    console.log(hasObj); // false

    Parameters

    Returns Promise<HandlerReturnType<Handler, Input, boolean>>

  • Return a new iterator that will include an item between every value returned by the iterator. The item can be a value or a function that, given the latest iterated value, returns a value of type Value> (or a Promise), i.e.

    const stream = await new AsyncArrayStream([0, 1, 2, 3, 4]).intersperse(5).collect();
    // stream = [0, 5, 1, 5, 2, 5, 3, 5, 4]

    or

    const stream = await new AsyncArrayStream([0, 1, 2, 3, 4])
    .intersperse((item) => item + 100)
    .collect();

    Type Parameters

    • Item

    Parameters

    Returns AsyncArrayStream<Input | Item, NarrowHandlerType<Handler, Input, Input | Item>>

  • Add a map operation to the iterator that will be resolved when the iterator is finalized. A map operation takes an item of type A and returns an item of type B (or Promise). A map function is different from forEach in that it should be pure and not have side effects, i.e.

    const stream = await new AsyncArrayStream([1, 2, 3, 4, 5])
    .map((item) => item * 2)
    .collect();
    console.log(stream); // [2, 4, 6, 8, 10]

    NOTE: Map functions change the type of the iterator, but if you call the function without reassigning the variable or chaining methods, then the type will be incorrect, i.e.

    const stream = new ArrayStream([1, 2, 3, 4, 5])
    stream.map(item => String.fromCharCode(item + 65));

    then the type of stream will be ArrayStream<number, Breaker<number>> instead of ArrayStream<string, Breaker<string>>. Instead, do one of these two:

    const stream = new ArrayStream([1, 2, 3, 4, 5])
    .map(item => String.fromCharCode(item + 65));

    or

    let stream = new ArrayStream([1, 2, 3, 4, 5]);
    stream = stream.map(item => String.fromCharCode(item + 65));

    Type Parameters

    • End

    Parameters

    Returns AsyncArrayStream<End, NarrowHandlerType<Handler, Input, End>>

  • Consume the iterator and return the item at the nth index (or null if it doesn't exist), i.e.

    const stream = await new AsyncArrayStream([100, 200, 300).nth(1);
    // stream = 200

    Parameters

    • n: number

    Returns Promise<HandlerReturnType<Handler, Input, null | Input>>

  • Consume the iterator and return a tuple of two arrays, the left being those that caused the function to return true, and the right the others, i.e.

    const [left, right] = await new AsyncArrayStream([1, 2, 3, 4, 5])
    .partition((item) => item % 2 === 0);
    console.log(left); // [2, 4]
    console.log(right); // [1, 3, 5]

    Parameters

    • fn: MaybeAsyncFn<Input, boolean>

    Returns Promise<HandlerReturnType<Handler, Input, [Input[], Input[]]>>

  • Asynchronously retrieves the next item from the stream without advancing the stream. If the stream has reached the end, it returns null, e.g.

    const stream = new AsyncArrayStream([1, 2, 3, 4, 5]);
    console.log(await stream.peek()); // 1
    console.log(await stream.peek()); // 1
    const items = await stream.collect();
    console.log(items); // [1, 2, 3, 4, 5]
    console.log(await stream.peek()); // null

    Returns Promise<null | Input>

  • Consume the iterator and collect all items into the chosen data structure starting from the first item, i.e.

    const stream = await new AsyncArrayStream([100, 200, 300)
    .reduce((acc, next) => acc + next, 0);
    console.log(stream) // 600

    Type Parameters

    • End

    Parameters

    Returns Promise<HandlerReturnType<Handler, Input, End>>

  • Consume the iterator and collect all items into the chosen data structure starting from the last item, i.e.

    const stream = await new AsyncArrayStream(["a", "b", "c"])
    .reduceRight((acc, next) => acc + next, "");
    console.log(stream) // "cba"

    Type Parameters

    • End

    Parameters

    Returns Promise<HandlerReturnType<Handler, Input, End>>