createSingleRequest
A factory function that creates a function wrapper allowing only a single async execution at a time.
An async function wrapped by the returned wrapper will not execute again while a call is in progress, but can be called again after the current execution completes.
debounce/throttle is effective in most cases for preventing duplicate calls of a function.
However, debounce/throttle does not guarantee the completion of async operations, so it has the following limitations:
- If the
debounce/throttletime is shorter than the API response time: the async operation may becalled againbefore it completes. - If the
debounce/throttletime is longer than the API response time: the async operation has completed but elements likebuttonsmay still bedisabled. - When you want
immediate feedback:debounce/throttledelays calls, making it limited for showing users animmediate response. - Since
debounce/throttleoperates based ontime, it is hard to determinewhether duplicate calls have occurred— onlyhow oftenit runs matters.
If you want to address these limitations, you can use createSingleRequest.
Code
Interface
typescript
function createSingleRequest(
key?: string
): <T, Args extends unknown[]>(
callback: (...args: Args) => Promise<T>
) => (...args: Args) => Promise<T | undefined>;
Usage
Basic Usage
typescript
import { createSingleRequest } from '@modern-kit/utils';
const singleRequest = createSingleRequest();
const wrappedFetch = singleRequest(fetchData);
wrappedFetch(); // executed
wrappedFetch(); // ignored because a call is in progress (returns undefined)
wrappedFetch(); // ignored because a call is in progress (returns undefined)
// Can be called again after the first call completes
Sharing a Single Lock Across Multiple Async Functions
typescript
import { createSingleRequest } from '@modern-kit/utils';
const singleRequest = createSingleRequest();
const wrappedSubmit = singleRequest(submitForm);
const wrappedSync = singleRequest(syncData);
// If wrappedSubmit is in progress, wrappedSync calls are also blocked (shared lock)
wrappedSubmit();
wrappedSync(); // returns undefined, not executed
Passing Arguments
typescript
import { createSingleRequest } from '@modern-kit/utils';
const singleRequest = createSingleRequest();
const wrappedFetch = singleRequest(async (id: number) => {
const res = await fetch(`/api/items/${id}`);
return res.json();
});
const data = await wrappedFetch(42); // 42 is passed to the callback.