Debounce Promise
To create a debounced function that returns a promise, delaying invoking the provided function until at least ms milliseconds have elapsed since the last time it was invoked, use the following steps:
- Every time the debounced function is invoked, clear the current pending timeout with
clearTimeout(), then use setTimeout() to create a new timeout that delays invoking the function until at least ms milliseconds has elapsed.
- Use
Function.prototype.apply() to apply the this context to the function and provide the necessary arguments.
- Create a new
Promise and add its resolve and reject callbacks to the pending promises stack.
- When
setTimeout() is called, copy the current stack (as it can change between the provided function call and its resolution), clear it and call the provided function.
- When the provided function resolves/rejects, resolve/reject all promises in the stack (copied when the function was called) with the returned data.
- Omit the second argument,
ms, to set the timeout at a default of 0 ms.
Here's the code for the debouncePromise() function:
const debouncePromise = (fn, ms = 0) => {
let timeoutId;
const pending = [];
return (...args) =>
new Promise((res, rej) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
const currentPending = [...pending];
pending.length = 0;
Promise.resolve(fn.apply(this, args)).then(
(data) => {
currentPending.forEach(({ resolve }) => resolve(data));
},
(error) => {
currentPending.forEach(({ reject }) => reject(error));
}
);
}, ms);
pending.push({ resolve: res, reject: rej });
});
};
Here's an example of how to use debouncePromise():
const fn = (arg) =>
new Promise((resolve) => {
setTimeout(resolve, 1000, ["resolved", arg]);
});
const debounced = debouncePromise(fn, 200);
debounced("foo").then(console.log);
debounced("bar").then(console.log);
// Will log ['resolved', 'bar'] both times