| name | request-throttling |
| description | 使用防抖、节流和批处理工具控制请求频率。处理高频事件(滚动、输入、窗口调整大小)和 API 速率限制的必要条件。 |
请求限流技能
何时使用此技能
当你需要以下操作时使用此技能:
- 减少 API 调用 来自频繁用户事件(搜索输入、调整大小)
- 批处理操作 以获得更好性能(数据库写入、文件上传)
- 速率限制 昂贵的计算(调整大小计算、重新渲染)
- 改进 UX 通过延迟工作直到用户完成交互
- 高效处理高频事件
三种模式
| 模式 | 用例 | 时机 |
|---|
| 防抖 | 等待用户停止 | 在 300ms 无活动后 |
| 节流 | 定期执行 | 每 100ms 无论如何 |
| 批处理 | 累积并处理 | 当批量大小达到时 |
快速入门
import { debounce } from '@x-oasis/debounce';
import { throttle } from '@x-oasis/throttle';
import { batchinator } from '@x-oasis/batchinator';
const searchAPI = debounce(async (query) => {
const results = await fetch(`/api/search?q=${query}`);
return results.json();
}, 300);
input.addEventListener('input', (e) => {
searchAPI(e.target.value);
});
const handleScroll = throttle(() => {
updateScrollPosition();
}, 100);
window.addEventListener('scroll', handleScroll);
const batch = batchinator(async (items) => {
await database.insertMany(items);
}, { maxSize: 10, timeout: 1000 });
batch.push(item1);
batch.push(item2);
模式 1:防抖
何时使用:你想等待直到用户停止做某事
import { debounce } from '@x-oasis/debounce';
const debouncedSearch = debounce(async (query: string) => {
const results = await api.search(query);
displayResults(results);
}, 300);
input.addEventListener('input', (e) => {
debouncedSearch(e.target.value);
});
真实例子:自动保存
const autoSave = debounce(async (content: string) => {
await api.saveDraft({ content });
showNotification('草稿已保存');
}, 1000);
editor.addEventListener('input', (e) => {
autoSave(e.target.value);
});
模式 2:节流
何时使用:你想定期执行但不太频繁
import { throttle } from '@x-oasis/throttle';
const handleScroll = throttle(() => {
const { scrollY } = window;
updateScrollIndicator(scrollY);
if (scrollY > documentHeight - viewportHeight - 500) {
loadMoreItems();
}
}, 100);
window.addEventListener('scroll', handleScroll);
真实例子:调整大小跟踪
const trackWindowResize = throttle(() => {
const { width, height } = window.innerWidth;
updateLayout(width, height);
}, 200);
window.addEventListener('resize', trackWindowResize);
模式 3:批处理
何时使用:你想累积项目并批量处理
import { batchinator } from '@x-oasis/batchinator';
const insertBatch = batchinator(
async (items: Item[]) => {
await database.insertMany(items);
console.log(`插入了 ${items.length} 项`);
},
{
maxSize: 100,
timeout: 5000,
}
);
async function processDataStream(stream) {
for await (const item of stream) {
insertBatch.push(item);
}
}
真实例子:分析事件跟踪
const trackEvents = batchinator(
async (events: Event[]) => {
await analytics.send(events);
},
{ maxSize: 50, timeout: 10000 }
);
document.addEventListener('click', (e) => {
trackEvents.push({
type: 'click',
target: e.target.id,
timestamp: Date.now(),
});
});
document.addEventListener('change', (e) => {
trackEvents.push({
type: 'change',
field: e.target.name,
value: e.target.value,
timestamp: Date.now(),
});
});
比较防抖 vs 节流
import { debounce } from '@x-oasis/debounce';
import { throttle } from '@x-oasis/throttle';
const logCall = () => console.log('called');
const debouncedLog = debounce(logCall, 300);
const throttledLog = throttle(logCall, 300);
debouncedLog();
debouncedLog();
debouncedLog();
throttledLog();
throttledLog();
throttledLog();
高级模式
高级 1:级联防抖
import { debounce } from '@x-oasis/debounce';
const updateSearchQuery = debounce(async (query: string) => {
const results = await api.search(query);
updateDisplay(results);
}, 300);
const cancel = updateSearchQuery.cancel?.();
高级 2:自适应节流
import { throttle } from '@x-oasis/throttle';
function createAdaptiveThrottle(handler, minInterval) {
let lastCall = Date.now();
return throttle(() => {
const now = Date.now();
const timeSinceLastCall = now - lastCall;
const isSlowDevice = navigator.deviceMemory < 4;
const interval = isSlowDevice ? minInterval * 2 : minInterval;
if (timeSinceLastCall >= interval) {
handler();
lastCall = now;
}
}, minInterval);
}
const scroll = createAdaptiveThrottle(() => {
updateLayout();
}, 100);
高级 3:带优先级的批处理
import { batchinator } from '@x-oasis/batchinator';
const batch = batchinator(
async (items) => {
const sorted = items.sort((a, b) => b.priority - a.priority);
await database.insertMany(sorted);
},
{ maxSize: 100, timeout: 5000 }
);
batch.push({ ...item, priority: 10 });
batch.push({ ...item, priority: 1 });
最佳实践
✅ 做法
const search = debounce(async (query) => {
const results = await api.search(query);
render(results);
}, 300);
const handleScroll = throttle(() => {
updateScrollIndicator();
}, Math.round(1000 / 60));
const batch = batchinator(
(items) => db.insertMany(items),
{ maxSize: 100, timeout: 5000 }
);
❌ 不做法
const debounceDelete = debounce(() => deleteData(), 500);
const throttle = throttle(() => updateAnimation(), 16);
batch.push(item1);
batch.push(item2);
常见陷阱
陷阱 1:忘记后边调用
import { debounce } from '@x-oasis/debounce';
const search = debounce(async (q) => {
results = await api.search(q);
}, 300, { trailing: true });
input.addEventListener('input', (e) => {
search(e.target.value);
});
陷阱 2:闭包的内存泄漏
for (const element of elements) {
element.addEventListener('input', debounce((e) => {
process(e.target.value);
}, 300));
}
const debouncedProcess = debounce((value) => process(value), 300);
for (const element of elements) {
element.addEventListener('input', (e) => {
debouncedProcess(e.target.value);
});
}
陷阱 3:错误的时间参数
const search = debounce(api.search, 50);
集成示例
使用 React
import { useEffect, useRef, useState } from 'react';
import { debounce } from '@x-oasis/debounce';
function SearchComponent() {
const [results, setResults] = useState([]);
const searchRef = useRef(
debounce(async (query) => {
const data = await api.search(query);
setResults(data);
}, 300)
);
return (
<input
onChange={(e) => searchRef.current(e.target.value)}
placeholder="搜索..."
/>
);
}
使用 Vue
import { debounce } from '@x-oasis/debounce';
export default {
data() {
return {
query: '',
results: [],
};
},
methods: {
handleSearch: debounce(function(query) {
this.api.search(query).then(results => {
this.results = results;
});
}, 300),
},
};
使用 Svelte
<script>
import { debounce } from '@x-oasis/debounce';
let query = '';
let results = [];
const handleSearch = debounce(async (q) => {
results = await api.search(q);
}, 300);
</script>
<input bind:value={query} on:input={() => handleSearch(query)} />
参考资料