setInterval与setTimeout执行结果不符合预期的问题
关于setInterval与setTimeout嵌套的延迟问题解析
问题描述
设置了一个间隔为4秒的setInterval调用函数fun1,fun1内部有个12秒的setTimeout用于打印“Hi”。首次打印“Hi”耗时16秒(4+12)可以理解,但之后每隔4秒就会打印一次“Hi”,不再出现16秒的延迟,仅遇到一次12秒的延迟。验证发现fun1每次都会执行,但为何不符合预期的“每次打印‘Hi’后间隔16秒再打印”?
代码示例:
function fun1() { setTimeout(() => { console.log("Hi"); }, 12000); } setInterval(async() => { await fun1(); }, 4000);
问题原因
1. await fun1()完全不起作用
fun1没有返回Promise对象,await一个非Promise值会直接同步完成,等于没有等待fun1内部的setTimeout执行。setInterval的回调函数会瞬间执行完毕,不会等待12秒的打印操作。
2. setInterval的运行逻辑是固定间隔触发
setInterval的核心逻辑是按预设的固定时间间隔(这里是4秒)重复触发回调,不管上一次回调有没有执行完成。具体时序拆解:
- 第0秒:启动
setInterval,计划第4秒第一次触发回调。 - 第4秒:触发回调调用
fun1,fun1立刻启动一个12秒的setTimeout(将在16秒打印“Hi”),同时因为await无效,回调直接结束。setInterval按计划,将在第8秒再次触发回调。 - 第8秒:第二次触发回调调用
fun1,又启动一个12秒的setTimeout(将在20秒打印“Hi”)。此时距离第一次打印(16秒)仅间隔4秒,所以会出现“每隔4秒打印一次”的现象。
简单说:每次setInterval触发都会新建一个独立的12秒定时器,这些定时器并行运行,到点就打印,自然会出现第一次16秒后,后续每4秒就有一个定时器到期的情况。
解决方案:实现预期的“打印后间隔16秒再打印”
如果要达到“每次打印‘Hi’后,间隔16秒再打印下一次”的效果,需要用递归式的setTimeout替代setInterval,同时让fun1返回Promise来实现真正的等待:
function fun1() { // 让fun1返回Promise,以便await能等待内部setTimeout完成 return new Promise(resolve => { setTimeout(() => { console.log("Hi"); resolve(); }, 12000); }); } async function loop() { await fun1(); // 等待打印Hi完成 setTimeout(loop, 4000); // 打印完成后,再等4秒启动下一次循环 } // 启动循环 loop();
这样每次都会等12秒的打印操作完成,再等待4秒,总共16秒后触发下一次打印,完全符合预期。
内容的提问来源于stack exchange,提问作者Dipesh
相关产品推荐
相关产品推荐

