为何setTimeout延迟0仍在for循环同步代码后执行?
为什么setTimeout延迟设为0仍会出现这种运行行为?
我了解这类问题已有相关讨论,但我认为本次问题具有独特性。为何将延迟设置为0时,仍会出现如下运行行为?
代码示例
for(var i = 0; i <3; i ++) { console.log(i, 'started'); setTimeout(()=> { console.log(i); },0) console.log(i, 'done'); } console.log('loop over');
输出结果
// 0 started // 0 done // 1 started // 1 done // 2 started // 2 done // loop over // 3 3 3
咱们把这个问题拆解开一步步看:
首先,JavaScript是单线程的,它必须先把当前调用栈里的所有同步任务执行完毕,才会去处理任务队列里的异步任务——这就是核心原因。
你这里用var声明变量i,var是函数/全局作用域,整个循环里的i其实是同一个全局变量。而setTimeout的回调函数属于异步任务,哪怕你把延迟设为0,它也不会立刻执行,而是会被放进任务队列排队,必须等当前所有同步代码(整个for循环+最后的console.log('loop over'))全部跑完,才会被取出执行。
等同步代码执行完成时,for循环已经终止了:最后一次循环i=2,执行i++后变成3,不再满足i<3的条件,所以此时i的值固定为3。这时候任务队列里的三个setTimeout回调才开始依次执行,它们引用的都是同一个全局i,自然都会输出3。
对应你提到的MDN逻辑:
这是因为
setTimeout()的回调会被加入到任务队列,只有当调用栈为空时,才会从任务队列中取出任务执行。哪怕延迟设为0,也只是表示“尽可能快地执行”,但必须等当前同步任务全部完成。
另外,如果想让每个回调输出对应的循环值,只需要把var换成let就行——let是块级作用域,每次循环都会创建一个独立的i变量,回调函数会绑定当前循环的那个i,最后就会输出0、1、2了。
内容的提问来源于stack exchange,提问作者Brandon
相关产品推荐
相关产品推荐

