基础防抖#
1
2
3
4
5
6
7
8
9
10
11
12
| const _debounce = (fn, delay, immediate = false) => {
let timer;
return (...args) => {
const callNow = immediate && !timer;
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
if (!immediate) fn(...args);
}, delay);
if (callNow) fn(...args);
};
};
|
带 Promise 的防抖#
鼠标拖拽时如果频繁触发防抖,而被包装函数会调 API 并返回 Promise,通常只希望最后一次真正执行。Lodash 的 debounce 要求 func 必须是函数:
1
2
3
| if (typeof func !== 'function') {
throw new TypeError('Expected a function');
}
|
异步场景需要稍作改造:
只保留最后一次 Promise#
1
2
3
4
5
6
7
8
9
| const debounce_promise_last = (fn, delay) => {
let timer = null;
return (...args) => {
clearTimeout(timer);
return new Promise((resolve) => {
timer = setTimeout(() => resolve(fn(...args)), delay);
});
};
};
|
每次调用都返回 Promise,延迟后统一结算#
1
2
3
4
5
6
7
8
9
10
11
12
13
| function debounce_promise_all(fn, delay = 0) {
let timer = null;
let resolves = [];
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
const result = fn(...args);
resolves.forEach((r) => r(result));
resolves = [];
}, delay);
return new Promise((r) => resolves.push(r));
};
}
|
API 场景:节流、取消、重试#
调 API 时除了防抖,节流也很常见。更复杂的情况还有取消和重试(常配合指数退避)。当 Promise 数量极大(我们曾遇到 1000+)时,通常需要:
- 分批执行,例如每批 20 个;
- 为每个 Promise 分配唯一 ID;
- 做好日志(我们曾打到 Splunk);
- 明确取消语义;
- Ajax 重试配合指数退避。
具体实现与业务强相关,这里不贴完整代码。