### Install Repeater.js Source: https://repeater.js.org Use npm to install the package in your project. ```bash npm install @repeaterjs/repeater ``` -------------------------------- ### Implementing Konami code with RxJS Source: https://repeater.js.org/docs/rationale An example of using RxJS observables to track key events and detect the Konami sequence. ```javascript import { Observable } from "rxjs"; import { takeWhile } from "rxjs/operators"; const keys = new Observable((subscriber) => { const listener = (ev) => { if (ev.key === "Escape") { subscriber.complete(); } else { subscriber.next(ev.key); } }; window.addEventListener("keyup", listener); return () => window.removeEventListener("keyup", listener); }); const konami = ["ArrowUp", "ArrowUp", "ArrowDown", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowLeft", "ArrowRight", "b", "a"]; let i = 0; let subscription = keys .pipe( takeWhile((key) => { if (key === konami[i]) { i++; } else { i = 0; } if (i >= konami.length) { console.log("KONAMI!!!"); return false; } return true; }), ) .subscribe(); ``` -------------------------------- ### new Repeater(executor, buffer?) Source: https://repeater.js.org/docs/repeater Constructs a new Repeater instance. The executor function is called when the repeater starts, providing push and stop capabilities. ```APIDOC ## constructor ### Description Creates a new Repeater instance. ### Parameters - **executor** (RepeaterExecutor) - Required - A function called when the repeater starts, providing push and stop arguments. - **buffer** (RepeaterBuffer) - Optional - An optional buffer object to store or drop values. ``` -------------------------------- ### Consume an async iterator with a generator Source: https://repeater.js.org/docs/safety This example illustrates how wrapping an iterator in an async generator can lead to resource leaks if the generator is terminated before the underlying iterator is consumed. ```javascript async function* positions(clicks) { for await (const c of clicks) { yield { x: c.clientX, y: c.clientY, }; } } (async function() { const clicks = listen(window, "click"); // adding listener! const pos = positions(clicks); // never mind we’re not interested in the positions of clicks. pos.return(); // πŸ’­πŸ’­πŸ’­ clicks.return is never called. })(); ``` -------------------------------- ### Define the RepeaterExecutor type Source: https://repeater.js.org/docs/repeater The executor function is invoked upon repeater start, providing push and stop capabilities. ```typescript type RepeaterExecutor = ( push: Push, stop: Stop, ) => PromiseLike | TReturn; ``` -------------------------------- ### Module entry points Source: https://repeater.js.org/docs/repeater Demonstrates how to import specific components from the package to optimize bundle size. ```typescript import { Repeater } from "@repeaterjs/repeater"; // class, buffers, and the static combinators import { Repeater } from "@repeaterjs/repeater/core"; // class and buffers only (no combinators) import { race, merge, zip, latest } from "@repeaterjs/repeater/combinators"; import { createDelay, createInterval, createTimeout } from "@repeaterjs/repeater/timers"; import { createSemaphore, createThrottle } from "@repeaterjs/repeater/limiters"; ``` -------------------------------- ### Import Timer Utilities Source: https://repeater.js.org/docs/utilities Import the timer creation functions from the package subpath. ```javascript import { createDelay, createInterval, createTimeout, } from "@repeaterjs/repeater/timers"; ``` -------------------------------- ### Import Limiter Utilities Source: https://repeater.js.org/docs/utilities Import the limiter creation functions from the package subpath. ```javascript import { createSemaphore, createThrottle } from "@repeaterjs/repeater/limiters"; ``` -------------------------------- ### Use createInterval Timer Source: https://repeater.js.org/docs/utilities Demonstrates using createInterval to execute code at specific intervals, with automatic cleanup upon loop termination. ```javascript import { createInterval } from "@repeaterjs/repeater/timers"; (async () => { let count = 0; for await (const _ of createInterval(1000)) { console.log(`tick ${count++}`); if (count >= 3) break; // breaking cleans up the underlying timer } })(); ``` -------------------------------- ### Create a Repeater with Error Handling Source: https://repeater.js.org/docs/overview Demonstrates using the push and stop functions to emit a value and then trigger an error. ```javascript const repeater = new Repeater((push, stop) => { push(1); stop(new Error("My error")); }); (async () => { console.log(await repeater.next()); // { value: 1, done: false } try { console.log(await repeater.next()); // This line throws an error. } catch (err) { console.log(err); // Error: My error } })(); ``` -------------------------------- ### Log timestamps with setInterval Source: https://repeater.js.org/docs/quickstart Demonstrates creating a repeater that pushes timestamps at a set interval and cleans up when the consumer stops. ```javascript import { Repeater } from "@repeaterjs/repeater"; const timestamps = new Repeater(async (push, stop) => { push(Date.now()); const interval = setInterval(() => push(Date.now()), 1000); await stop; clearInterval(interval); }); (async function() { let i = 0; for await (const timestamp of timestamps) { console.log(timestamp); i++; if (i >= 10) { console.log("ALL DONE!"); break; // triggers clearInterval above } } })(); ``` -------------------------------- ### Synchronize Producers and Consumers with Await Source: https://repeater.js.org/docs/safety Awaiting the push function forces the executor to wait until the consumer calls next, effectively synchronizing the producer and consumer. ```javascript const numbers = new Repeater(async (push, stop) => { console.log("starting..."); for (let i = 1; i <= 4; i++) { console.log(`pushing ${i}`); await push(i); } console.log("stopping..."); stop(); }); (async function() { // starting... console.log(await numbers.next()); // pushing 1 // { value: 1, done: false } console.log(await numbers.next()); // pushing 2 // { value: 2, done: false } console.log(await numbers.next()); // pushing 3 // { value: 3, done: false } console.log(await numbers.next()); // pushing 4 // { value: 4, done: false } // stopping... console.log(await numbers.next()); // { done: true } })(); ``` -------------------------------- ### Repeater.prototype[Symbol.asyncDispose] / [Symbol.dispose] Source: https://repeater.js.org/docs/repeater Implements the explicit resource management protocol for automatic cleanup. ```APIDOC ## Repeater.prototype[Symbol.asyncDispose] / Repeater.prototype[Symbol.dispose] ### Description These methods implement the explicit resource management protocol so that `await using` and `using` automatically call `return` when a repeater goes out of scope. `[Symbol.asyncDispose]` awaits `return`; `[Symbol.dispose]` calls it synchronously. ``` -------------------------------- ### Enqueue Multiple Values in a Repeater Source: https://repeater.js.org/docs/overview Shows how to push multiple values sequentially and close the repeater without an error. ```javascript const repeater = new Repeater((push, stop) => { push(1); push(2); push(3); push(4); stop(); }); (async () => { console.log(await repeater.next()); // { value: 1, done: false } console.log(await repeater.next()); // { value: 2, done: false } console.log(await repeater.next()); // { value: 3, done: false } console.log(await repeater.next()); // { value: 4, done: false } console.log(await repeater.next()); // { done: true } })(); ``` -------------------------------- ### Handling errors with the throw method Source: https://repeater.js.org/docs/error_handling Demonstrates how to catch errors injected into a repeater via the throw method using a try-catch block around the push operation. ```javascript const repeater = Repeater(async (push) => { for (let i = 0; i < 10; i++) { try { await push(i); } catch (err) { console.log(err); console.log("Hello I caught your error πŸ‘€"); } } }); (async function() { console.log(await repeater.next()); // { value: 1, done: false }; console.log(await repeater.throw(new Error("Hello? πŸ“ž"))); // Error: Hello? πŸ“ž // Hello I caught your error πŸ‘€ // { value: 2, done: false } console.log(await repeater.next()); // { value: 3, done: false }; console.log(await repeater.next()); // { value: 4, done: false }; })(); ``` -------------------------------- ### Synchronize iterators with Repeater.zip Source: https://repeater.js.org/docs/combinators Uses Repeater.zip to pull values from multiple iterators in lockstep, awaiting results via Promise.all. ```javascript import { Repeater, SlidingBuffer } from "@repeaterjs/repeater"; import { createDelay } from "@repeaterjs/repeater/timers"; const keys = new Repeater(async (push, stop) => { const listener = (ev) => push(ev.key); window.addEventListener("keydown", listener); await stop; window.removeEventListener("keydown", listener); }, new SlidingBuffer(1)); (async function() { for await (const [key] of Repeater.zip([keys, createDelay(1000)])) { console.log(key); // will log the latest key every second } })(); ``` -------------------------------- ### Repeater.prototype[Symbol.asyncDispose] / [Symbol.dispose] Source: https://repeater.js.org/docs/repeater Implements explicit resource management for automatic cleanup via using or await using. ```typescript class Repeater { [Symbol.asyncDispose](): Promise>; [Symbol.dispose](): void; } ``` -------------------------------- ### Create a repeater from a websocket Source: https://repeater.js.org/docs/quickstart Wraps a WebSocket connection in a repeater to handle incoming messages and lifecycle events. ```javascript import { Repeater } from "@repeaterjs/repeater"; const socket = new WebSocket("ws://echo.websocket.org"); const messages = new Repeater(async (push, stop) => { socket.onmessage = (ev) => push(ev.data); socket.onerror = () => stop(new Error("WebSocket error")); socket.onclose = () => stop(); await stop; socket.close(); }); (async function() { for await (const message of messages) { console.log(message); if (message === "close") { console.log("Closing!"); break; // closes the socket } } })(); socket.onopen = () => { socket.send("hello"); // "hello" socket.send("world"); // "world" socket.send("close"); // "close", "Closing!" }; ``` -------------------------------- ### Timer Utilities Source: https://repeater.js.org/docs/utilities Functions for creating async iterators that handle timing operations like delays, intervals, and timeouts. ```APIDOC ## createDelay(wait) ### Description Creates a repeater that yields the current timestamp `wait` milliseconds after each call to `next`. ### Parameters - **wait** (number) - Required - The delay duration in milliseconds. ## createInterval(wait) ### Description Yields the current timestamp every `wait` milliseconds, functioning like `setInterval` as an async iterator. ### Parameters - **wait** (number) - Required - The interval duration in milliseconds. ## createTimeout(wait) ### Description Throws a `TimeoutError` if `next` is not called within `wait` milliseconds of the previous pull. ### Parameters - **wait** (number) - Required - The timeout duration in milliseconds. ``` -------------------------------- ### Listen for Konami Code Source: https://repeater.js.org/docs/quickstart Uses a repeater to manage DOM event listeners, allowing for clean removal when the consumer stops. ```javascript import { Repeater } from "@repeaterjs/repeater"; const keys = new Repeater(async (push, stop) => { const listener = (ev) => { if (ev.key === "Escape") { stop(); } else { push(ev.key); } }; window.addEventListener("keyup", listener); await stop; window.removeEventListener("keyup", listener); }); const konami = ["ArrowUp", "ArrowUp", "ArrowDown", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowLeft", "ArrowRight", "b", "a"]; (async function() { let i = 0; for await (const key of keys) { if (key === konami[i]) { i++; } else { i = 0; } if (i >= konami.length) { console.log("KONAMI!!!"); break; // removes the keyup listener } } })(); ``` -------------------------------- ### Use Repeater.prototype.next Source: https://repeater.js.org/docs/repeater The next method retrieves the next value from the sequence or sends a value back to the repeater. ```typescript class Repeater { next(value?: PromiseLike | TNext): Promise>; } ``` -------------------------------- ### Handle Overflow with Synchronous Errors Source: https://repeater.js.org/docs/safety Calling push in a fire-and-forget manner will throw synchronous errors when the pending push limit is exceeded, surfacing bottlenecks immediately. ```javascript const ys = new Repeater(async (push, stop) => { const listener = () => push(window.scrollY); // ⚠️ Might throw an error! window.addEventListener("scroll", listener); await stop; window.removeEventListener("scroll", listener); }); ys.next(); ``` -------------------------------- ### DroppingBuffer Source: https://repeater.js.org/docs/repeater The DroppingBuffer class allows repeaters to push unlimited values by dropping the latest values when the capacity is reached. ```APIDOC ## DroppingBuffer ### Description Allows repeaters to push unlimited values without having pushes wait or throw errors. When the buffer reaches the specified capacity, the buffer drops the latest values passed to the buffer. ### Constructor `new DroppingBuffer(capacity: number)` ### Parameters - **capacity** (number) - The maximum number of values that can be added to the buffer before the buffer drops values. ``` -------------------------------- ### Limiter Utilities Source: https://repeater.js.org/docs/utilities Functions for managing concurrency and rate limiting using async iterators. ```APIDOC ## createSemaphore(limit) ### Description Yields up to `limit` tokens at a time to cap concurrency. Each token provides a `release()` method to return capacity. ### Parameters - **limit** (number) - Required - The maximum number of concurrent tokens. ## createThrottle(wait, options?) ### Description Yields up to `options.limit` tokens per `wait`-millisecond window to cap the rate of work. ### Parameters - **wait** (number) - Required - The window duration in milliseconds. - **options** (object) - Optional - Configuration object containing `limit`. ``` -------------------------------- ### Create a naive async iterator for events Source: https://repeater.js.org/docs/safety This implementation demonstrates a common pattern that is prone to memory leaks if the returned iterator's return method is not explicitly called. ```javascript function listen(target, name) { const events = []; const nexts = []; function listener(ev) { const next = nexts.shift(); if (next == null) { events.push(ev); } else { next({ value: ev, done: false }); } }; console.log("adding listener!"); target.addEventListener(name, listener); return { next() { const ev = events.shift(); if (ev == null) { return new Promise((next) => nexts.push(next)); } return Promise.resolve({ value: ev, done: false }); }, return() { nexts.forEach((next) => next({ done: true })); console.log("removing listener!"); target.removeEventListener(name, listener); return Promise.resolve({ done: true }); }, [Symbol.asyncIterator]() { return this; }, }; } ``` -------------------------------- ### Repeater.zip(iterables) Source: https://repeater.js.org/docs/combinators Synchronizes multiple async iterables by awaiting a result from each in lockstep and yielding an array of the results. ```APIDOC ## Repeater.zip(iterables) ### Description Takes an iterable of async iterables, awaits a result from every iterable using Promise.all, and yields the resulting array. This is useful for synchronizing multiple iterators in lockstep. ### Parameters - **iterables** (Iterable) - Required - An iterable containing the async iterables to be zipped. ``` -------------------------------- ### Demonstrating async generator limitations Source: https://repeater.js.org/docs/rationale Shows the syntax error encountered when attempting to yield values from within a nested callback inside an async generator. ```javascript async function* messages(url) { const socket = new WebSocket(url); socket.onmessage = (ev) => { // can’t make the outer generator yield from here. yield ev.data // this line throws a syntax error }; } ``` -------------------------------- ### Repeater.zip Source: https://repeater.js.org/docs/repeater Creates a repeater that yields a tuple of values taken from each iterable for each iteration. When any iterable finishes, the repeater returns a tuple of values from each iterator. ```APIDOC ## Repeater.zip ### Description A repeater which yields a tuple of values taken from each iterable for each iteration. When any iterable finishes, the repeater returns a tuple of values from each iterator. Promises are treated as an async iterator which returns the value when the promise fulfills. ### Parameters - **contenders** (Iterable>) - Required - An iterable of async iterables, iterables or promises. ``` -------------------------------- ### SlidingBuffer Source: https://repeater.js.org/docs/repeater The SlidingBuffer class allows repeaters to push unlimited values by dropping the earliest values when the capacity is reached. ```APIDOC ## SlidingBuffer ### Description Allows repeaters to push unlimited values without having pushes wait or throw errors. When the buffer reaches the specified capacity, the buffer drops the earliest values passed to the buffer. ### Constructor `new SlidingBuffer(capacity: number)` ### Parameters - **capacity** (number) - The maximum number of values that can be added to the buffer before the buffer drops values. ``` -------------------------------- ### Dispose a repeater with await using Source: https://repeater.js.org/docs/safety Uses 'await using' to ensure the repeater's cleanup logic is executed when the variable goes out of scope. ```typescript import { Repeater } from "@repeaterjs/repeater"; async function untilFirstClick() { await using clicks = new Repeater(async (push, stop) => { const listener = () => push("click"); window.addEventListener("click", listener); await stop; window.removeEventListener("click", listener); }); for await (const click of clicks) { return click; // `await using` calls clicks.return() on the way out } } ``` -------------------------------- ### Repeater.latest static method definition Source: https://repeater.js.org/docs/repeater Defines the Contender type and the signature for the Repeater.latest static method. ```typescript type Contender = AsyncIterable | T> | Iterable | T> | PromiseLike | T; Repeater.latest = function ( contenders: Iterable>, ): Repeater; ``` -------------------------------- ### Buffer Values with SlidingBuffer Source: https://repeater.js.org/docs/safety Passing a buffer instance to the Repeater constructor allows storing values and dropping them based on the buffer strategy, preventing overflow errors. ```javascript import { Repeater, SlidingBuffer } from "@repeaterjs/repeater"; const ys = new Repeater(async (push, stop) => { const listener = () => push(window.scrollY); // πŸ™‚ will never throw window.addEventListener("scroll", listener); await stop; window.removeEventListener("scroll", listener); }, new SlidingBuffer(20)); ys.next(); ``` -------------------------------- ### Repeater.prototype.next(value?) Source: https://repeater.js.org/docs/repeater Retrieves the next value from the repeater. Optionally sends a value back to the repeater, which resolves the previous push call. ```APIDOC ## next(value?) ### Description Returns an object with done and value properties. Can be used to send a value back to the repeater. ### Parameters - **value** (TNext) - Optional - The value to send to the repeater, which resolves the previous push call. ### Return Value - **Promise>** - A promise that resolves to an object containing 'done' (boolean) and 'value' (the pushed value or executor return value). ``` -------------------------------- ### Create an Inverted Repeater Timer Source: https://repeater.js.org/docs/inverted_repeaters This pattern uses a Repeater to manage asynchronous timeouts by retaining promise resolution functions and awaiting the push call. ```javascript const timer = new Repeater(async (push, stop) => { const timers = []; let stopped = false; stop.then(() => (stopped = true)); try { while (!stopped) { let resolve; let reject; const promise = new Promise((resolve1, reject1) => { resolve = resolve1; reject = reject1; }); const timer = { resolve, reject, timeout: setTimeout(() => { resolve(Date.now()); timers.unshift(); }, 1000), }; timers.push(timer); await push(promise); } } finally { for (const timer of timers) { timer.reject(new Error("This error is never seen")); clearTimeout(timer.timeout); } } }); ``` -------------------------------- ### Repeater.latest Source: https://repeater.js.org/docs/repeater A static method that creates a repeater yielding a tuple of values from each provided contender. It waits for every iterable to yield at least once before emitting. ```APIDOC ## Repeater.latest ### Description Creates a repeater that yields a tuple of values taken from each iterable in the provided collection. The repeater yields a tuple whenever any of the contenders yields a value, but waits until every iterable has yielded at least once. ### Signature `Repeater.latest(contenders: Iterable>): Repeater` ### Parameters - **contenders** (Iterable>) - Required - An iterable of async iterables, iterables, or promises. ### Return Value - **Repeater** - A repeater which yields a tuple of values from each iterable. ``` -------------------------------- ### Repeater.zip Method Source: https://repeater.js.org/docs/repeater Yields a tuple of values taken from each iterable for each iteration. Returns a tuple of values when any iterable finishes. ```typescript type Contender = AsyncIterable | T> | Iterable | T> | PromiseLike | T; Repeater.zip = function ( contenders: Iterable>, ): Repeater; ``` -------------------------------- ### Repeater.prototype.throw Source: https://repeater.js.org/docs/repeater Causes the previous push call to reject with the given error, allowing for error propagation within the repeater. ```APIDOC ## Repeater.prototype.throw ### Description The `throw` method causes the previous `push` call to reject with the given error. If the executor fails to handle the `error`, the `throw` method rethrows the error and finishes the repeater. ### Parameters - **error** (any) - Optional - The error to send to the repeater. ### Return Value A promise which fulfills to the next iteration result if the repeater handles the `error`, and otherwise rejects with the given `error`. ``` -------------------------------- ### FixedBuffer Source: https://repeater.js.org/docs/repeater The FixedBuffer class allows repeaters to push a set number of values without waiting or throwing errors. Once the capacity is reached, the repeater reverts to its default behavior. ```APIDOC ## FixedBuffer ### Description Allows repeaters to push a set number of values without having pushes wait or throw errors. When the limit is reached, the repeater behaves as it would without a buffer. ### Constructor `new FixedBuffer(capacity: number)` ### Parameters - **capacity** (number) - The maximum number of values that can be added to the buffer. ``` -------------------------------- ### Implement FixedBuffer Source: https://repeater.js.org/docs/repeater A buffer that holds a fixed number of values before applying standard repeater backpressure. ```typescript class FixedBuffer implements RepeaterBuffer { constructor(capacity: number); } ``` -------------------------------- ### Race async iterators with Repeater.race Source: https://repeater.js.org/docs/combinators Uses Repeater.race with createTimeout to enforce a time limit on each iteration of an async iterator. ```javascript import { Repeater } from "@repeaterjs/repeater"; import { createTimeout } from "@repeaterjs/repeater/timers"; const numbers = new Repeater(async (push) => { await push(1); await push(2); await new Promise((resolve) => setTimeout(resolve, 2000)); await push(3); }); (async () => { try { for await (const num of Repeater.race([numbers, createTimeout(1000)])) { console.log(num); // 1, 2 } } catch (err) { console.log(err); // TimeoutError: 1000 ms elapsed } })(); ``` -------------------------------- ### Implement SlidingBuffer Source: https://repeater.js.org/docs/repeater A buffer that drops the earliest values when the capacity is exceeded. ```typescript class SlidingBuffer implements RepeaterBuffer { constructor(capacity: number); } ``` -------------------------------- ### Await Repeater State Changes Source: https://repeater.js.org/docs/overview Utilizes the push and stop promises to synchronize execution flow within the repeater executor. ```javascript const repeater = new Repeater(async (push, stop) => { console.log("repeater started!"); await push(1); console.log("pushed 1"); await push(2); console.log("pushed 2"); await stop; console.log("done"); }); (async () => { console.log(await repeater.next()); // repeater started! // { value: 1, done: false } console.log(await repeater.next()); // "pushed 1" // { value: 2, done: false } console.log(await repeater.return()); // "pushed 2" // "done" // { done: true } })(); ``` -------------------------------- ### Define the Repeater class Source: https://repeater.js.org/docs/repeater The Repeater class constructor accepts an executor function and an optional buffer. ```typescript class Repeater { constructor( executor: RepeaterExecutor, buffer?: RepeaterBuffer, ); } ``` -------------------------------- ### Repeater.prototype.return Source: https://repeater.js.org/docs/repeater Prematurely finishes the repeater, dropping pending values and ignoring the executor's return value. ```APIDOC ## Repeater.prototype.return ### Description The `return` method prematurely finishes the repeater. When returned, any pending values on the repeater are dropped. Additionally, the `executor`’s return value is ignored. ### Parameters - **value** (PromiseLike | TReturn) - Optional - The value to return. If `value` is a promise, `return` awaits the value before returning it. ### Return Value A promise which fulfills to an object with the following properties: - **done** (boolean) - true - **value** (any) - the value passed to `return`. ``` -------------------------------- ### Implement DroppingBuffer Source: https://repeater.js.org/docs/repeater A buffer that drops the latest values when the capacity is exceeded. ```typescript class DroppingBuffer implements RepeaterBuffer { constructor(capacity: number); } ``` -------------------------------- ### Track latest values with Repeater.latest Source: https://repeater.js.org/docs/combinators Returns a repeater yielding an array of the latest values from each iterable whenever any source yields a value. Requires all iterators to produce at least one value before the first next call fulfills. ```javascript import { Repeater } from "@repeaterjs/repeater"; const leftCount = new Repeater(async (push, stop) => { let i = 0; push(i++); const listener = (ev) => push(i++); window.addEventListener("click", listener); await stop; window.removeEventListener("click", listener); }); const rightCount = new Repeater(async (push, stop) => { let i = 0; push(i++); const listener = (ev) => push(i++); window.addEventListener("contextmenu", listener); await stop; window.removeEventListener("contextmenu", listener); }); (async function() { for await (const [left, right] of Repeater.latest([leftCount, rightCount])) { if (left === right) { console.log("You have left- and right-clicked the exact same number of times"); } else if (left > right) { console.log(`You have left-clicked ${left - right} more times than right-clicked`); } else { console.log(`You have right-clicked ${right - left} more times than left-clicked`); } } })(); ``` -------------------------------- ### Repeater.prototype.throw Source: https://repeater.js.org/docs/repeater Injects an error into the repeater, causing the previous push call to reject. ```typescript class Repeater { throw(error?: any): Promise>; } ``` -------------------------------- ### Repeater.race Source: https://repeater.js.org/docs/combinators Repeater.race takes an iterable of async iterables and races results from each, yielding the value which fulfilled first. It can be used with utilities like createTimeout to enforce time limits on async iterations. ```APIDOC ## Repeater.race(iterables) ### Description Takes an iterable of async iterables and races results from each, yielding the value which fulfilled first. If a promise is passed, the entire iteration is raced against that promise. ### Parameters - **iterables** (Iterable) - Required - An iterable containing async iterables or promises to race. ``` -------------------------------- ### Repeater.prototype.return Source: https://repeater.js.org/docs/repeater Prematurely finishes the repeater and drops pending values. ```typescript class Repeater { return(value?: PromiseLike | TReturn): Promise>; } ``` -------------------------------- ### Define the Push function type Source: https://repeater.js.org/docs/repeater The push function enqueues values for consumers and returns a promise that resolves based on the repeater's state. ```typescript type Push = ( value: PromiseLike | T, ) => Promise; ``` -------------------------------- ### Repeater.merge Method Source: https://repeater.js.org/docs/repeater Yields values from each iterable as they settle. Returns when all provided iterables have finished. ```typescript Repeater.merge = function ( contenders: Iterable, ): Repeater< T extends AsyncIterable | Iterable ? U extends PromiseLike ? V : U : T extends PromiseLike ? U : T >; ``` -------------------------------- ### Repeater.race Source: https://repeater.js.org/docs/repeater Creates a repeater that yields the first settling value from each iterable for each iteration. If any iterable finishes, the repeater returns with that iterable's final value. ```APIDOC ## Repeater.race ### Description A repeater which yields the first settling value from each iterable for each iteration. If any iterables finishes, the repeater returns with that iterable’s final value. Promises are treated as an async iterator which returns the value when the promise fulfills. ### Parameters - **contenders** (Iterable) - Required - An iterable of async iterables, iterables or promises. ``` -------------------------------- ### Use createSemaphore Limiter Source: https://repeater.js.org/docs/utilities Limits concurrent execution of an asynchronous function using a semaphore. ```javascript import { createSemaphore } from "@repeaterjs/repeater/limiters"; // Run at most two `fetch`es concurrently. const sem = createSemaphore(2); async function fetchThrottled(url) { const { value: token } = await sem.next(); try { return await fetch(url); } finally { token.release(); } } ``` -------------------------------- ### Define RepeaterBuffer Interface Source: https://repeater.js.org/docs/repeater The base interface for all repeater buffer implementations. ```typescript export interface RepeaterBuffer { full: boolean; empty: boolean; add(value: unknown): void; remove(): unknown; } ``` -------------------------------- ### Repeater.latest(iterables) Source: https://repeater.js.org/docs/combinators Returns a repeater that yields an array of the latest values from each provided async iterable whenever any of them yields a new value. ```APIDOC ## Repeater.latest(iterables) ### Description Takes an iterable of async iterables and returns a repeater which yields an array of the latest values from each iterable whenever any of them yields a value. The first call to next will not fulfill until all iterators have produced at least one value. ### Parameters - **iterables** (Iterable) - Required - An iterable containing the async iterables to monitor. ``` -------------------------------- ### Implement a safe listener with Repeaters Source: https://repeater.js.org/docs/safety This version uses the Repeater constructor to ensure lazy execution, preventing the registration of event listeners until the iterator is actually consumed. ```javascript function listen(target, name) { return new Repeater(async (push, stop) => { const listener = (ev) => push(ev); console.log("adding listener!"); target.addEventListener(name, listener); await stop; console.log("removing listener!"); target.removeEventListener(name, listener); }); } ``` -------------------------------- ### Repeater.merge(iterables) Source: https://repeater.js.org/docs/combinators Combines multiple async iterables or promises into a single repeater that yields values in the order they occur. ```APIDOC ## Repeater.merge(iterables) ### Description Takes an iterable of async iterables or promises and returns a repeater which yields values whenever any of the inputs yield values or fulfill. This is useful for consuming multiple asynchronous sources concurrently. ### Parameters - **iterables** (Iterable) - Required - A collection of async iterables or promises to merge. ### Usage Example ```javascript import { Repeater } from "@repeaterjs/repeater"; // Merging async iterables for await (const value of Repeater.merge([iterable1, iterable2])) { console.log(value); } // Merging promises const p1 = new Promise((resolve) => setTimeout(() => resolve("a"), 300)); const p2 = new Promise((resolve) => setTimeout(() => resolve("b"), 200)); for await (const value of Repeater.merge([p1, p2])) { console.log(value); // b, a } ``` ``` -------------------------------- ### Merge Promises with Repeater.merge Source: https://repeater.js.org/docs/combinators Yields values from an array of promises as they fulfill. ```javascript import { Repeater } from "@repeaterjs/repeater"; (async function() { const a = new Promise((resolve) => setTimeout(() => resolve("a"), 300)); const b = new Promise((resolve) => setTimeout(() => resolve("b"), 200)); const c = new Promise((resolve) => setTimeout(() => resolve("c"), 100)); const d = new Promise((resolve) => setTimeout(() => resolve("d"), 400)); for await (const letter of Repeater.merge([a, b, c, d])) { console.log(letter); // c, b, a, d } })(); ``` -------------------------------- ### Race an iterator against a promise Source: https://repeater.js.org/docs/combinators Races an entire iteration against a promise, requiring manual cleanup of the timer in a finally block. ```javascript import { Repeater } from "@repeaterjs/repeater"; import { createTimeout } from "@repeaterjs/repeater/timers"; const numbers = new Repeater(async (push) => { await new Promise((resolve) => setTimeout(resolve, 800)); await push(1); await new Promise((resolve) => setTimeout(resolve, 800)); await push(2); await new Promise((resolve) => setTimeout(resolve, 800)); await push(3); }); const timer = createTimeout(2000); (async function() { try { for await (const num of Repeater.race([numbers, timer.next()])) { console.log(num); // 1, 2 } } catch (err) { console.log(err); // TimeoutError: 2000 ms elapsed } finally { await timer.return(); } })(); ``` -------------------------------- ### Repeater.merge Source: https://repeater.js.org/docs/repeater Creates a repeater that yields values from each iterable as they settle. When all iterables finish, the repeater returns with the final value of the last iterator which finished. ```APIDOC ## Repeater.merge ### Description A repeater which yields values from each iterable as they settle. When all iterables finish, the repeater returns with the final value of the last iterator which finished. Promises are treated as an async iterator which yields the value when the promise fulfills. ### Parameters - **contenders** (Iterable) - Required - An iterable of async iterables, iterables or promises. ``` -------------------------------- ### Handle rejected promises passed to push Source: https://repeater.js.org/docs/error_handling If a promise passed to push rejects, the repeater finishes and drops subsequent values. ```javascript const repeater = new Repeater(async (push, stop) => { await push("a"); await push("b"); await push("c"); await push(new Promise((_, reject) => { setTimeout(() => reject(new Error("A rejection passed to push ⏰")), 100); })); // these values are dropped await push("e"); await push("f"); // this error is ignored stop(new Error("Stop in the name of love 😘")); }); (async function() { try { for await (const letter of repeater) { console.log(letter); // "a", "b", "c" } } catch (err) { console.log(err); // Error: A rejection passed to push ⏰ } finally { console.log(await repeater.next()); // { done: true } } })(); ``` -------------------------------- ### Convert an observable to an async iterator Source: https://repeater.js.org/docs/quickstart Adapts an RxJS Subject to an async iterator, ensuring subscriptions are cleaned up properly. ```javascript import { Subject } from "rxjs"; import { Repeater } from "@repeaterjs/repeater"; const observable = new Subject(); const repeater = new Repeater(async (push, stop) => { const subscription = observable.subscribe({ next: (value) => push(value), error: (err) => stop(err), complete: () => stop(), }); await stop; subscription.unsubscribe(); }); (async function() { try { for await (const value of repeater) { console.log("Value: ", value); } } catch (err) { console.log("Error caught: ", err); } })(); observable.next(1); // Value: 1 observable.next(2); // Value: 2 observable.error(new Error("Hello from observable")); // Error caught: Error: Hello from observable ``` -------------------------------- ### Merge Async Iterables with Repeater.merge Source: https://repeater.js.org/docs/combinators Consumes multiple async iterators concurrently, yielding values in the order they occur. ```javascript import { Repeater } from "@repeaterjs/repeater"; const leftClicks = new Repeater(async (push, stop) => { const listener = (ev) => push("left"); window.addEventListener("click", listener); await stop; window.removeEventListener("click", listener); }); const rightClicks = new Repeater(async (push, stop) => { const listener = (ev) => push("right"); window.addEventListener("contextmenu", listener); await stop; window.removeEventListener("contextmenu", listener); }); (async function() { for await (const click of Repeater.merge([leftClicks, rightClicks])) { console.log(click); // left, left, right, left, right } })(); ``` -------------------------------- ### Repeater.race Method Source: https://repeater.js.org/docs/repeater Yields the first settling value from each iterable for each iteration. Returns when any of the provided iterables finish. ```typescript Repeater.race = function ( contenders: Iterable, ): Repeater< T extends AsyncIterable | Iterable ? U extends PromiseLike ? V : U : never >; ``` -------------------------------- ### Define the Stop type Source: https://repeater.js.org/docs/repeater The stop argument acts as both a function to terminate the repeater and a promise that resolves upon termination. ```typescript type Stop = ((error?: any) => undefined) & Promise; ``` -------------------------------- ### Handle errors thrown by the executor Source: https://repeater.js.org/docs/error_handling Executor errors take precedence over other errors and cause the repeater to stop immediately. ```javascript const repeater = new Repeater((push, stop) => { push("a"); push("b"); push("c"); // this error is dropped stop(new Error("My error")); // this error takes priority throw new Error("This executor is busted ☠️"); }); (async function() { try { for await (const letter of repeater) { console.log(letter); // "a", "b", "c" } } catch (err) { console.log(err); // Error: This executor is busted ☠️ } finally { console.log(await repeater.next()); // { done: true } } })(); ``` -------------------------------- ### MAX_QUEUE_LENGTH constant Source: https://repeater.js.org/docs/repeater Defines the maximum number of pending operations allowed on a single repeater. ```typescript const MAX_QUEUE_LENGTH = 1024; ``` -------------------------------- ### RepeaterOverflowError Source: https://repeater.js.org/docs/repeater An error class thrown when the push or next operations exceed the maximum allowed queue length. ```APIDOC ## RepeaterOverflowError ### Description Thrown when the `push` function or `next` method is called on a repeater with too many pending operations. ### Constructor `new RepeaterOverflowError(message: string)` ### Parameters - **message** (string) - Required - A human-readable description of the error. ``` -------------------------------- ### Stop a repeater with an error Source: https://repeater.js.org/docs/error_handling Pass an error object to the stop function to terminate the repeater and reject the final iteration. ```javascript const repeater = new Repeater((push, stop) => { for (let i = 0; i < 100; i++) { push(i); } stop(new Error("Stop in the name of love 😘")); }); (async function() { try { console.log(await repeater.next()); // { value: 0, done: true } console.log(await repeater.next()); // { value: 1, done: true } console.log(await repeater.next()); // { value: 2, done: true } console.log(await repeater.return()); // This line throws an error. } catch (err) { console.log(err); // Error: Stop in the name of love 😘 } finally { console.log(await repeater.next()); // { done: true } console.log(await repeater.next()); // { done: true } } })(); ``` -------------------------------- ### RepeaterOverflowError class definition Source: https://repeater.js.org/docs/repeater Represents an error thrown when the internal queue length limit is exceeded. ```typescript class RepeaterOverflowError extends Error { constructor(message: string); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.