Skip to content

Instantly share code, notes, and snippets.

@snipsnipsnip
Last active September 17, 2025 06:25
Show Gist options
  • Save snipsnipsnip/2f88ff56400adcdb897da71e4ab8162d to your computer and use it in GitHub Desktop.
Save snipsnipsnip/2f88ff56400adcdb897da71e4ab8162d to your computer and use it in GitHub Desktop.
reusingRace<A,B>(factoryA: () => Promise<A>, factoryB: () => Promise<B>): () => Promise<A | B>
//! 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;
}
}
//! 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