setTimeout 为什么不能保证及时执行
理解 JavaScript 事件循环机制和 setTimeout 的执行时机
问题
为什么 setTimeout 设置的延迟时间不能保证准时执行?
解答
setTimeout 的执行时机取决于 JavaScript 的事件循环机制(Event Loop)和主线程的繁忙程度。
执行流程
当浏览器的 JS 引擎遇到 setTimeout 时:
- 定时器注册:
setTimeout被 timer 模块接管,不会立即放入任务队列 - 同步任务执行:主线程继续执行同步代码
- 等待时间到达:timer 模块在设置的延迟时间后,将回调函数放入异步任务队列
- 等待执行栈清空:只有当主线程的执行栈为空时,才会从任务队列中读取任务
- 执行回调:回调函数被放入执行栈并执行
console.log('开始');
setTimeout(() => {
console.log('setTimeout 回调');
}, 100);
// 模拟耗时操作
for (let i = 0; i < 1000000000; i++) {}
console.log('结束');
// 输出顺序:
// 开始
// 结束
// setTimeout 回调(实际延迟远超 100ms)
为什么会延迟
即使设置了 100ms 的延迟,实际执行时间可能更长,因为:
- 同步任务可能耗时很长,阻塞主线程
- 任务队列中可能有其他待执行的任务
setTimeout的回调必须等待执行栈完全清空
关键点
setTimeout只保证在指定时间后将回调放入任务队列,不保证立即执行- 回调的实际执行时间 = 设置的延迟时间 + 主线程空闲等待时间
- Event Loop 机制:执行栈清空后才会从任务队列中读取任务
- 长时间运行的同步任务会阻塞
setTimeout的执行
目录