export default class Queue implements WritableStream, AsyncGenerator { readonly #promises: Promise[] = [] readonly #resolvers: ((item: T) => void)[] = [] readonly #observers: ((item: T) => void)[] = [] #closed = false #locked = false readonly #stream: WritableStream static #__add( promises: Promise[], resolvers: ((item: X) => void)[] ): void { promises.push( new Promise((resolve) => { resolvers.push(resolve) }) ) } static #__enqueue( closed: boolean, promises: Promise[], resolvers: ((item: X) => void)[], item: X ): void { if (!closed) { if (!resolvers.length) Queue.#__add(promises, resolvers) const resolve = resolvers.shift()! // eslint-disable-line @typescript-eslint/no-non-null-assertion resolve(item) } } constructor() { const closed = this.#closed const promises = this.#promises const resolvers = this.#resolvers this.#stream = new WritableStream({ write(item: T): void { Queue.#__enqueue(closed, promises, resolvers, item) }, }) } #add(): void { return Queue.#__add(this.#promises, this.#resolvers) } enqueue(item: T): void { return Queue.#__enqueue(this.#closed, this.#promises, this.#resolvers, item) } dequeue(): Promise { if (!this.#promises.length) this.#add() const item = this.#promises.shift()! // eslint-disable-line @typescript-eslint/no-non-null-assertion return item } isEmpty(): boolean { return !this.#promises.length } isBlocked(): boolean { return !!this.#resolvers.length } get length(): number { return this.#promises.length - this.#resolvers.length } async next(): Promise> { const done = false const value = await this.dequeue() for (const observer of this.#observers) { observer(value) } return { done, value } } return(): Promise> { return new Promise(() => { // empty }) } throw(err: Error): Promise> { return new Promise((_resolve, reject) => { reject(err) }) } [Symbol.asyncIterator](): AsyncGenerator { return this } [Symbol.asyncDispose](): Promise { return this.close() } get locked(): boolean { return this.#stream.locked } abort(reason?: Error): Promise { return this.#stream.abort(reason) } close(): Promise { return this.#stream.close() } getWriter(): WritableStreamDefaultWriter { return this.#stream.getWriter() } }