( key: string, fn: () => Promise<T>, settleTimeoutMs: number = DEFAULT_SETTLE_TIMEOUT_MS )
| 28 | * caller will join it. |
| 29 | */ |
| 30 | export function coalesceLocally<T>( |
| 31 | key: string, |
| 32 | fn: () => Promise<T>, |
| 33 | settleTimeoutMs: number = DEFAULT_SETTLE_TIMEOUT_MS |
| 34 | ): Promise<T> { |
| 35 | const existing = inflight.get(key) as Promise<T> | undefined |
| 36 | if (existing) return existing |
| 37 | |
| 38 | let timer: ReturnType<typeof setTimeout> | undefined |
| 39 | const evict = () => { |
| 40 | if (inflight.get(key) === guarded) inflight.delete(key) |
| 41 | } |
| 42 | |
| 43 | const guarded: Promise<T> = Promise.race([ |
| 44 | (async () => { |
| 45 | try { |
| 46 | // Defer fn() to a microtask so a synchronous throw surfaces as a |
| 47 | // rejection after `guarded` and the timer are initialized. Calling it |
| 48 | // inline would run the finally below during construction, touching |
| 49 | // `guarded` in its temporal dead zone and masking fn's real error. |
| 50 | return await Promise.resolve().then(fn) |
| 51 | } finally { |
| 52 | clearTimeout(timer) |
| 53 | evict() |
| 54 | } |
| 55 | })(), |
| 56 | new Promise<never>((_, reject) => { |
| 57 | timer = setTimeout(() => { |
| 58 | evict() |
| 59 | reject(new CoalesceSettleTimeoutError(key, settleTimeoutMs)) |
| 60 | }, settleTimeoutMs) |
| 61 | timer.unref?.() |
| 62 | }), |
| 63 | ]) |
| 64 | |
| 65 | inflight.set(key, guarded) |
| 66 | return guarded |
| 67 | } |
| 68 | |
| 69 | export function __resetCoalesceLocallyForTests(): void { |
| 70 | inflight.clear() |
no test coverage detected