--- title: debouncePromise tags: function,promise,advanced --- Creates a debounced function that returns a promise, but delays invoking the provided function until at least `ms` milliseconds have elapsed since the last time it was invoked. All promises returned during this time will return the same data. - Each time the debounced function is invoked, clear the current pending timeout with `clearTimeout()` and 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. ```js 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 }); }); }; ``` ```js 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 ```