You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为什么JavaScript中setTimeout函数在循环内无法正常工作?

setTimeout在for循环中仅等待一次就执行所有回调的原因及解决办法

原因

JavaScript是单线程语言,遵循事件循环机制:同步任务会优先执行,异步任务(比如setTimeout的回调)会被放入任务队列,等同步任务全部完成后才会依次执行。

你的for循环是同步代码,会在瞬间执行完5次迭代,每次迭代都把一个setTimeout任务放进队列,且每个任务的延迟都是1000ms。所以1秒后,所有队列里的回调会被一次性取出执行,看起来就像只等了一次就全跑完了。

另外注意:你代码里用了let声明i,它在for循环的每次迭代中都会创建一个独立的块级作用域,所以最终console.log(i)会输出0、1、2、3、4(如果用var的话,所有回调都会拿到循环结束后的i=5),但这和“同时执行”的问题无关——不管用let还是var,所有回调都是1秒后同时触发。

解决办法

方法1:给每个setTimeout设置递增的延迟时间

把延迟时间改成i * 1000,让每个回调的触发时间依次错开:

for (let i = 0; i < 5; i++) {
    setTimeout(function(){
        console.log(i);
    }, i * 1000)
}

这样第一个回调0秒后执行,第二个1秒后,以此类推,实现每隔1秒输出一个数。

方法2:使用闭包(针对var声明的场景)

如果是用var声明i(老代码场景),可以用闭包保存每次迭代的i值,同时配合递增延迟:

for (var i = 0; i < 5; i++) {
    (function(index) {
        setTimeout(function(){
            console.log(index);
        }, index * 1000)
    })(i)
}

方法3:使用forEach循环

forEach的回调函数会为每个元素创建独立作用域,同样可以配合递增延迟:

[0,1,2,3,4].forEach(function(i) {
    setTimeout(function(){
        console.log(i);
    }, i * 1000)
})

方法4:用async/await实现串行执行

如果需要严格等待上一个回调执行完再触发下一个,可以用Promise包装setTimeout,配合async/await:

function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
}

async function run() {
    for (let i = 0; i < 5; i++) {
        await delay(1000)
        console.log(i)
    }
}

run()

这种方式会真正等待1秒后输出一个数,再等1秒输出下一个,而不是一次性设置所有延迟。


内容的提问来源于stack exchange,提问作者JUAN JOSE HUERTAS BOTACHE

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 03:01:25