为何clearTimeout会立即执行?slow_guard函数实现技术问询
关于
slow_guard函数中clearTimeout立即执行的疑问解答 嘿,我来帮你拆解这个问题~先把你的代码完整贴出来方便分析:
function slow_guard(timeout, fn) { var timer = setTimeout(function() { console.log('Function lasts for more than ' + timeout/1000 + ' sec.!'); }, timeout + 1); var start = new Date().getTime(); fn(); var end = new Date().getTime(); var time = end - start; console.log('Execution time: ' + time); clearTimeout(timer); }
为什么clearTimeout会“立即”执行?
核心原因是JavaScript的单线程执行机制,再加上你调用的fn()是同步函数:
- 当你运行
slow_guard时,首先会创建一个定时器,把它的警告回调放进“待办任务队列”里,得等当前所有同步任务做完才会轮到它执行。 - 接下来会直接执行
fn(),如果fn是同步的(没有异步请求、定时器这类操作),它会在当前线程里一口气跑完,不会中途暂停。 - 等
fn完全执行完,才会继续往下走:计算执行时间、打印日志,然后调用clearTimeout(timer)。这时候定时器的回调还没机会触发呢——因为线程刚忙完同步任务,还没轮到处理“待办队列”里的定时器,所以clearTimeout直接就把这个定时器取消了。
如果fn是异步函数(比如里面有setTimeout或者AJAX请求),问题会更突出:fn会先发起异步操作,然后立刻返回,后面的clearTimeout马上就执行了,根本等不到异步操作完成,这时候定时器到点就会触发警告,完全不是你想要的效果。
怎么修正才能达到预期效果?
要准确检测函数(不管同步还是异步)的执行时间,最好用Promise来处理,确保我们能等到函数真正执行完再取消定时器:
async function slow_guard(timeout, fn) { let timer = setTimeout(() => { console.log(`Function lasts for more than ${timeout/1000} sec.!`); }, timeout); try { const start = performance.now(); // 比new Date精度更高 // 不管fn是同步还是异步,await都会等它执行完成 await fn(); const end = performance.now(); console.log(`Execution time: ${Math.round(end - start)}ms`); } finally { // 不管fn成功还是失败,都取消定时器 clearTimeout(timer); } }
如果确定fn一定是同步函数,那你的代码逻辑其实没问题,只是可以优化一下细节:
- 把定时器延迟改成
timeout而不是timeout+1,避免误判 - 用
performance.now()代替new Date().getTime(),时间精度更高
调整后的同步版:
function slow_guard(timeout, fn) { const timer = setTimeout(() => { console.log(`Function lasts for more than ${timeout/1000} sec.!`); }, timeout); const start = performance.now(); fn(); const end = performance.now(); const time = end - start; console.log(`Execution time: ${Math.round(time)}ms`); // 这里要注意:如果fn执行时间真的超过timeout,定时器已经触发警告了,clearTimeout就没用了 // 这刚好符合我们要警告的场景~ clearTimeout(timer); }
内容的提问来源于stack exchange,提问作者g1un
相关产品推荐
相关产品推荐

