JavaScript节流函数异常:console.log仅打印一次求排查
节流函数异常排查:console.log(json)仅打印一次
问题代码如下:
function throttle(cb, delay) { let shouldWait = false; return function() { if (shouldWait) return; cb(); shouldWait = true; setTimeout(() => { shouldWait = false; }, delay); }; } const fetchTodos = async() => { fetch("https://jsonplaceholder.typicode.com/todos/1") .then((response) => response.json()) .then((json) => console.log(json)); }; const fetchTodosThrottled = throttle(fetchTodos, 1000); for (let i = 0; i < 1000; i++) { fetchTodosThrottled(); }
问题原因
你的节流函数是立即执行型节流(首次调用直接执行,冷却期内调用直接忽略),但核心问题出在调用时机上:
- 同步循环会在极短时间内完成1000次
fetchTodosThrottled()调用 - 第一次调用时,
shouldWait为false,执行fetchTodos后立刻将shouldWait设为true - 剩下的999次调用都因
shouldWait为true直接返回,根本没触发fetchTodos setTimeout要等1000ms后才会把shouldWait改回false,但此时循环早已结束,没有后续调用,所以只会打印一次
解决方案
方案1:调整调用方式(适配现有节流函数)
如果希望在冷却期后持续执行请求,不能用同步循环,改用异步触发的方式:
const fetchTodosThrottled = throttle(fetchTodos, 1000); let count = 0; const intervalId = setInterval(() => { if (count >= 5) { // 控制执行次数 clearInterval(intervalId); return; } fetchTodosThrottled(); count++; }, 100); // 用短间隔触发,确保节流函数能捕获到冷却后的调用
方案2:修改节流函数,支持尾触发
如果希望同步循环的最后一次调用能在冷却期结束后执行,可以给节流函数增加尾触发逻辑:
function throttle(cb, delay) { let shouldWait = false; let timeoutId = null; return function(...args) { if (shouldWait) { // 覆盖之前的尾调用定时器,只保留最后一次 clearTimeout(timeoutId); timeoutId = setTimeout(() => { cb.apply(this, args); }, delay); return; } cb.apply(this, args); shouldWait = true; setTimeout(() => { shouldWait = false; clearTimeout(timeoutId); timeoutId = null; }, delay); }; }
修改后,同步循环的1000次调用中,第一次会立即执行,最后一次调用会在首次执行的1000ms后触发,总共打印两次结果。
补充说明
原始节流函数的逻辑本身没问题,但它是无尾触发的立即执行节流,不适合短时间内大量同步调用的场景——因为循环执行速度远快于节流冷却时间,冷却期还没到循环就结束了,自然只有首次调用生效。
内容的提问来源于stack exchange,提问作者jpxcar
相关产品推荐
相关产品推荐

