Last active
September 17, 2025 06:25
-
-
Save snipsnipsnip/2f88ff56400adcdb897da71e4ab8162d to your computer and use it in GitHub Desktop.
reusingRace<A,B>(factoryA: () => Promise<A>, factoryB: () => Promise<B>): () => Promise<A | B>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //! SPDX-License-Identifier: Apache-2.0 | |
| //! snipsnipsnip.github.io | |
| // See also: https://doc.rust-lang.org/std/iter/struct.Peekable.html | |
| export class PeekableAsyncIterator<T, TReturn> implements AsyncIterator<T, TReturn, undefined> { | |
| private last: Promise<IteratorResult<T, TReturn>> | undefined | |
| constructor(private readonly wrapped: AsyncIterator<T, TReturn, undefined>) {} | |
| peek(): Promise<IteratorResult<T, TReturn>> { | |
| return this.last ??= this.wrapped.next(); | |
| } | |
| next(): Promise<IteratorResult<T, TReturn>> { | |
| if (this.last) { | |
| const result = this.last; | |
| this.last = undefined; | |
| return result; | |
| } | |
| return this.wrapped.next(); | |
| } | |
| [Symbol.asyncIterator](): this { | |
| return this; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //! SPDX-License-Identifier: Apache-2.0 | |
| //! snipsnipsnip.github.io | |
| // TIL: Rather than using utility like this, it's better to change API if possible to conform to the CQRS. | |
| // Bad: `interface MyQueue<T> { waitAndDequeue(): Promise<T> }` | |
| // Good: `interface MyQueue<T> { waitReady(): Promise<void>; dequeue(): T | undefined }` | |
| // If you don't control the API, or it's an `AsyncIterator` already, I hope something like `PeekableAsyncIterator` work for you. | |
| // See also: https://jsr.io/@std/async/doc/mux-async-iterator/~/MuxAsyncIterator | |
| /** | |
| * A utility that wraps `Promise.race` and two factories of promises. | |
| * Losing promises in `Promise.race` continue running and consume resources. | |
| * This utility tries to solve the problem by reusing unfulfilled promises in | |
| * subsequent races. | |
| * @param factoryA A factory of promise that consumes some resources. | |
| * @param factoryB Another factory of promise that consumes some resources. | |
| * @returns A closure that resolves to the result of promise created by either of factories. Can be called repeatedly. | |
| */ | |
| export function reusingRace<A, B = A>( | |
| factoryA: (this: void) => Promise<A>, | |
| factoryB: (this: void) => Promise<B>, | |
| ): () => Promise<A | B> { | |
| let promiseA: Promise<[number, A]> | undefined; | |
| let promiseB: Promise<[number, B]> | undefined; | |
| return async () => { | |
| promiseA ??= factoryA().then((a) => [0, a]); | |
| promiseB ??= factoryB().then((b) => [1, b]); | |
| const [i, result] = await Promise.race([promiseA, promiseB]); | |
| if (i === 0) { | |
| promiseA = undefined; | |
| } else { | |
| promiseB = undefined; | |
| } | |
| return result; | |
| }; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment