async-streamify
Version:
Stream and serialize nested promises and async iterables over HTTP, workers, etc
121 lines (120 loc) • 3.4 kB
JavaScript
/**
* 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),
};
}
}