UNPKG

async-streamify

Version:

Stream and serialize nested promises and async iterables over HTTP, workers, etc

121 lines (120 loc) 3.4 kB
/** * A class that implements an async iterable with buffering capabilities. * Allows pushing values into a buffer that can be consumed asynchronously. * * @template T - The type of values stored in the buffer * * @example * ```typescript * const buffer = new BufferedAsyncIterable<number>(); * * // Push values * buffer.push(1); * buffer.push(2); * buffer.close(); // Below will keep awaiting until this is called. * * // Consume values * for await (const value of buffer) { * console.log(value); // 1, 2 * } * ``` */ export default class BufferedAsyncIterable { constructor() { Object.defineProperty(this, "buffer", { enumerable: true, configurable: true, writable: true, value: [] }); Object.defineProperty(this, "resolvers", { enumerable: true, configurable: true, writable: true, value: [] }); Object.defineProperty(this, "isClosed", { enumerable: true, configurable: true, writable: true, value: false }); /** * Optional callback that is invoked when a consumer is waiting for values */ Object.defineProperty(this, "onWait", { enumerable: true, configurable: true, writable: true, value: void 0 }); /** * Optional callback that is invoked when a consumer calls `next()` */ Object.defineProperty(this, "onNext", { enumerable: true, configurable: true, writable: true, value: void 0 }); } /** * Pushes a new value into the buffer. * If there are waiting consumers, the first one will be resolved with this value. * * @param value - The value to push into the buffer * @returns void */ push(value) { this.buffer.push(value); const resolver = this.resolvers.shift(); if (resolver) { resolver({ value: this.buffer.shift(), done: false }); } } /** * Marks the iterable as done, resolving all waiting consumers with done=true * * @returns void */ close() { this.isClosed = true; for (const resolver of this.resolvers) { resolver({ value: undefined, done: true }); } this.resolvers = []; } /** * Returns a promise that resolves with the next value in the buffer. * If the buffer is empty and not done, the promise will wait for the next push. * * @returns Promise<IteratorResult<T>> */ next() { if (this.onNext) this.onNext(); return new Promise((resolve) => { if (this.buffer.length) { resolve({ value: this.buffer.shift(), done: false }); } else if (this.isClosed) { resolve({ value: undefined, done: true }); } else { this.resolvers.push(resolve); if (this.onWait) this.onWait(); } }); } /** * Implementation of the AsyncIterator interface * * @returns AsyncIterator<T> */ [Symbol.asyncIterator]() { return { next: this.next.bind(this), }; } }