一个js面试题
问题
一个js面试题
由大模型辅助整理,保留原意,优化结构与可读性。
问题概述
这是一道关于 setTimeout 执行时机的 JavaScript 面试题,代码在 for 循环中多次调用 setTimeout,并在循环结束后又用 alert 阻塞了程序,提问者想理解最终的输出顺序和背后原理。
涉及技术
JavaScript、事件循环(Event Loop)、setTimeout
期望结果
理解 setTimeout 的延时并不代表“精确到点执行”,而是“最早可以进入执行队列的时间”,并搞清楚同步代码、alert 阻塞和异步回调之间的执行顺序关系。
回答
原始回答
setTimeout是延时执行,但是他其实是不准确的。后面的毫秒数代表着他在多少毫秒后会进入执行队列里面。如果你前面有一个占用了非常多时间的运算。setTimeout就会延时特别久。他只在当前队列执行完之后,才尝试执行。
for (var i = 0; i < 5; i++) {
//延迟执行
setTimeout(function() {
console.log(new Date, i);
}, 1000);
}
console.log(new Date, i);
alert("我们用alert挂起一下程序,你等待两三秒再确定");
//执行到这里,这个队列才算是执行完了。然后才会尝试执行你的settimeout延时的程序,看看是不是可以调用了。不信你看看时间
AI 优化 · 回答整理
解答思路
setTimeout 设置的毫秒数并不是“精确的执行时间”,而是“多久之后可以被放入任务队列等待执行”;只有当前同步代码(包括被 alert 阻塞的那段)全部执行完毕,JS 引擎才会去看队列里有没有到时间的回调可以执行。
示例代码
for (var i = 0; i < 5; i++) {
// 延迟执行
setTimeout(function () {
console.log(new Date(), i);
}, 1000);
}
console.log(new Date(), i);
alert("我们用 alert 挂起一下程序,你等待两三秒再确定");
// 执行到这里,这个同步队列才算是执行完了。
// 然后才会尝试执行 setTimeout 延时的回调,看看是否已经到时间可以调用了。
详细说明
- 循环体内的
setTimeout只是把回调“预约”进任务队列,1000ms 后到期,但到期并不代表立刻执行,而是“有资格被执行”。 console.log(new Date, i)(循环外)会在循环结束后立刻同步执行,此时i已经是5。alert会阻塞主线程,只要弹窗没被关闭,JS 引擎就无法去处理任务队列,哪怕setTimeout的 1000ms 早就到了。- 等
alert被确认关闭、同步代码执行完毕后,引擎才会依次把队列中已到期的 5 个setTimeout回调拿出来执行,此时它们打印的时间会明显晚于“预约”时的时间,而i的值则和闭包捕获的变量有关(var声明下 5 次回调共享同一个i,此时都已经变成5)。
小结
setTimeout 只保证“不早于”指定时间执行,真正的执行时机还要看主线程何时空闲;如果主线程被同步代码或 alert 阻塞,异步回调只能排队等待,这正是 JS 单线程 + 事件循环模型的核心体现。
由大模型辅助整理,保留原意,补充结构与代码格式。
回答转载自 SegmentFault