JavaScript中嵌套setTimeout及Promise循环执行结果不符问题咨询
问题分析与解决办法
为什么会出现这样的输出?
我来帮你拆解下这个问题的核心原因:你的代码里两层嵌套的setTimeout和Promise的resolve时机不匹配,导致了输出顺序不符合预期:
- 首先要肯定的是,你用立即执行函数(IIFE)捕获循环中的
i值,这部分是正确的,所以start和end能按3→2→1的顺序打印,这部分没问题。 - 关键问题出在外层
setTimeout的逻辑:它在1秒后执行时,会先启动内层的1秒setTimeout,然后立刻打印end i并调用resolve()。这意味着Promise的状态会在打印end i后就变成已完成,Promise链会直接进入下一个then(也就是下一个start的逻辑),但内层的setTimeout还在等待执行,它的回调会在当前Promise已经resolve之后才触发。 - 具体的时间线可以梳理成这样:
- 第1秒:打印
start 3,外层setTimeout触发,启动内层1秒的timeout,然后打印end 3,resolve Promise,进入下一个then - 第2秒:打印
start 2,外层setTimeout触发,启动内层1秒timeout,打印end 2,resolve Promise,进入下一个then;同时i=3的内层timeout到时间,打印timeout - 第3秒:打印
start 1,外层setTimeout触发,启动内层1秒timeout,打印end 1,resolve Promise,进入最后一个then;同时i=2的内层timeout到时间,打印timeout - 第4秒:最后一个then执行,打印
execution ends;随后i=1的内层timeout到时间,打印timeout
- 第1秒:打印
这就是为什么最后一个timeout会在execution ends之后出现的原因。
解决办法:调整resolve的时机
如果你希望每个循环的完整逻辑(包括内层的timeout打印)都执行完毕后,再进入下一个Promise链环节,只需要把resolve()移到内层setTimeout的回调函数里,这样整个Promise会等待两层超时都完成后才会标记为已完成:
修改后的代码如下:
var i = 3 var p = Promise.resolve(i) while (i > 0) { (i => { p = p.then(() => { return new Promise(function (resolve, reject) { console.log('start', i) setTimeout(function () { setTimeout(() => { console.log('timeout') // 把resolve移到这里,等内层timeout执行完再完成当前Promise resolve() }, 1000); console.log('end', i) }, 1000) }) }) })(i) i-- } p = p.then(data => console.log('execution ends'))
修改后的输出会符合你大概率预期的顺序:
start 3 end 3 timeout start 2 end 2 timeout start 1 end 1 timeout execution ends
这样每个循环的start→end→timeout会完整执行后,才会进入下一个循环的逻辑,最后execution ends会在所有timeout打印完成后出现。
内容的提问来源于stack exchange,提问作者Ketan Bodarya
相关产品推荐
相关产品推荐

