为何setTimeout执行顺序不符合预期?JS异步机制疑问
为什么这段JavaScript代码的输出顺序是2、3、1?
代码示例
setTimeout(() => console.log("1"), 1000) const startTime = Date.now() while (Date.now() - startTime < 5000); setTimeout(() => console.log("2"), 0) setTimeout(() => console.log("3"), 0)
实际输出
2 3 1
原因解析
核心在于JavaScript的单线程事件循环机制,以及setTimeout的任务调度逻辑:
首先执行同步代码:
- 调用第一个
setTimeout后,浏览器会记录“1秒后将这个回调加入宏任务队列”,但此时主线程继续往下走,进入了while循环。 - 这个
while循环直接阻塞主线程5秒——因为JS是单线程,只要同步代码没执行完,事件循环就完全没机会处理任何异步任务,包括检查定时器是否到期。
- 调用第一个
5秒后
while循环结束,主线程终于有空处理后续逻辑:- 这时候第一个
setTimeout的1秒早就到了,但浏览器之前根本没机会把它的回调放进宏任务队列——只有主线程空闲时,浏览器才会扫描到期的定时器,把对应的回调加入队列。 - 紧接着执行后面两个
setTimeout,它们的延迟是0,浏览器会立刻把这两个回调按顺序加入宏任务队列(先2后3)。
- 这时候第一个
当所有同步代码执行完毕,主线程开始处理宏任务队列:
- 队列里的顺序是「2的回调 → 3的回调 → 1的回调」(1的回调是在这之后才被浏览器检测到到期并加入队列的)。
- 因此会依次输出2、3,最后才是1。
总结:定时器到期只是获得了“排队资格”,但必须等主线程空闲,浏览器才会把它的回调放进任务队列;后面两个setTimeout是在主线程空闲前就被调度入队,所以排在了第一个定时器回调的前面。
内容的提问来源于stack exchange,提问作者Vadim Sheremetov
相关产品推荐
相关产品推荐

