An async countdown latch is a one-shot gate that stays closed until a fixed number of events have happened, then releases every waiter at once. You tell it up front how many events to wait for; each event calls countDown(), and anyone who called wait() resumes the moment the last event reports in. It is the async cousin of a countdown latch from concurrent programming (for example Java's CountDownLatch) — the tool for "run this once all N of those have finished."
Implement asyncBarrierLatch(count). It returns three functions: countDown() records one event, wait() returns a promise that resolves when the latch opens, and remaining() reports how many events are still needed.
type Latch = {
countDown(): void; // record one event; the count-th call opens the latch
wait(): Promise<void>; // resolves when the latch opens (now, if already open)
remaining(): number; // events still needed before the latch opens
};
function asyncBarrierLatch(count: number): Latch;
const latch = asyncBarrierLatch(3);
latch.wait().then(() => console.log('all ready'));
latch.remaining(); // 3
latch.countDown(); // remaining 2
latch.countDown(); // remaining 1
latch.countDown(); // remaining 0 -> logs "all ready"
const latch = asyncBarrierLatch(2);
const a = latch.wait();
const b = latch.wait();
latch.countDown();
latch.countDown(); // a and b both resolve together
latch.wait(); // already open -> resolves immediately
latch.countDown(); // no-op: nothing left to count
latch.remaining(); // 0
countDown() has run exactly count times, not on the first one.wait() promises can be outstanding at once; they all resolve at the single moment the latch opens.wait() after the latch is open returns an already-resolved promise. The latch never closes again.remaining() reaches zero, further countDown() calls do nothing: the count never goes negative and waiters are never released twice.count is an integer of at least 1. Each wait() resolves with undefined.countDown() drives every resolution. You never need setTimeout.