Singleflight collapses concurrent calls that share a key into one execution: while a call for that key is in flight, every other caller receives the same pending promise instead of starting the work again. The moment that promise settles, the key is released, so the next call runs the work afresh.
The classic use is request de-duplication. Ten components mount at once and each asks for the current user; without singleflight that is ten identical network requests. Wrap the fetch in singleflightDedup keyed by 'user' and it fires once, with all ten awaiting the same response. The same guard fits any expensive, idempotent job — refreshing an auth token, warming a cache, rebuilding a report — where a burst of callers would otherwise repeat identical work.
function singleflightDedup(): SingleFlight;
interface SingleFlight {
// Runs fn() for `key`. While that promise is pending, any other
// do(key, ...) returns the SAME promise without calling fn again.
// On settle (resolve or reject) the key is freed.
do<T>(key: string, fn: () => Promise<T>): Promise<T>;
}
const sf = singleflightDedup();
const load = () => fetch('/api/user').then((r) => r.json());
const a = sf.do('user', load);
const b = sf.do('user', load); // 'user' is already in flight
a === b; // true — load() ran once; both callers await one request
const sf = singleflightDedup();
await sf.do('user', load); // runs load(), then frees 'user'
sf.do('user', load); // 'user' free again → runs load() a second time
sf.do('posts', load); // different key → its own independent flight
fn is a thunk — it takes no arguments and returns a promise; pass any parameters to the work through a closure.The full solution is part of Premium
Walkthrough, edge cases, complexity notes, and the runnable editor unlock with a Premium subscription.
Submissions are part of Premium
Unlock community code, comments, reactions, and framework-specific approaches.
Singleflight collapses concurrent calls that share a key into one execution: while a call for that key is in flight, every other caller receives the same pending promise instead of starting the work again. The moment that promise settles, the key is released, so the next call runs the work afresh.
The classic use is request de-duplication. Ten components mount at once and each asks for the current user; without singleflight that is ten identical network requests. Wrap the fetch in singleflightDedup keyed by 'user' and it fires once, with all ten awaiting the same response. The same guard fits any expensive, idempotent job — refreshing an auth token, warming a cache, rebuilding a report — where a burst of callers would otherwise repeat identical work.
function singleflightDedup(): SingleFlight;
interface SingleFlight {
// Runs fn() for `key`. While that promise is pending, any other
// do(key, ...) returns the SAME promise without calling fn again.
// On settle (resolve or reject) the key is freed.
do<T>(key: string, fn: () => Promise<T>): Promise<T>;
}
const sf = singleflightDedup();
const load = () => fetch('/api/user').then((r) => r.json());
const a = sf.do('user', load);
const b = sf.do('user', load); // 'user' is already in flight
a === b; // true — load() ran once; both callers await one request
const sf = singleflightDedup();
await sf.do('user', load); // runs load(), then frees 'user'
sf.do('user', load); // 'user' free again → runs load() a second time
sf.do('posts', load); // different key → its own independent flight
fn is a thunk — it takes no arguments and returns a promise; pass any parameters to the work through a closure.The full solution is part of Premium
Walkthrough, edge cases, complexity notes, and the runnable editor unlock with a Premium subscription.
Submissions are part of Premium
Unlock community code, comments, reactions, and framework-specific approaches.
Unlock the solution & editor
Runnable editor + tests
Solve in the browser with instant Jest feedback.
Detailed solutions
Walkthroughs, edge cases, and complexity notes.
Multi-framework variants
React, Vue, Vanilla, Angular — same question, different stacks.