30秒学会 JavaScript 片段 · 2022年3月2日

30秒学会 JavaScript 片段 – Debounce a JavaScript function and return a promise

Debouncing is a technique used to limit the number of times a function is called. We’ve previously seen how to debounce a function, but what if we want to return a promise instead?

Same as before, we can use timeouts to create a delay as needed. We need to clear the current pending timeout, using clearTimeout(), and create a new timeout with setTimeout() each time the debounced function is invoked. Similarly, we can use Function.prototype.apply() to apply the this context to the function and provide the necessary arguments.

However, we also need to keep track of all pending promises and resolve/reject them when the function is invoked. To do that, we can create a pending array and add the resolve and reject callbacks of each promise to it.

When the function is invoked, the current pending array will have to be copied, as it can change between the function call and its resolution. Then, we can clear the pending array and call the provided function.

Finally, when the function resolves/rejects, we can resolve/reject all promises in the copied array with the returned data. This means that all promises created in the meantime will resolve/reject with the same data.

代码实现

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 });
    });
};

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

翻译自:https://www.30secondsofcode.org/js/s/debounce-promise