防抖动一个JavaScript函数并返回一个Promise
防抖动是一种技术,用于限制函数被调用的次数。我们之前已经看过如何防抖动一个函数,但如果我们想要返回一个Promise呢?
和之前一样,我们可以使用定时器根据需要创建延迟。我们需要使用clearTimeout()
来清除当前挂起的定时器,并在每次调用防抖动函数时使用setTimeout()
来创建一个新的定时器。同样地,我们可以使用Function.prototype.apply()
将this
上下文应用到函数并提供必要的参数。
然而,我们还需要跟踪所有挂起的Promise,并在函数被调用时解决/拒绝它们。为此,我们可以创建一个pending
数组,并将每个Promise的resolve
和reject
回调添加到其中。
当函数被调用时,当前的pending
数组必须被复制,因为它可能在函数调用和解决之间发生变化。然后,我们可以清空pending
数组并调用提供的函数。
最后,当函数解决/拒绝时,我们可以使用返回的数据解决/拒绝复制数组中的所有Promise。这意味着在此期间创建的所有Promise都将使用相同的数据解决/拒绝。
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);
// 两次都会打印 ['resolved', 'bar']