setTimeout 运行机制

理解 setTimeout 的执行时机和 JavaScript 事件循环机制

问题

为什么 setTimeout(fn, 0) 不会立即执行?理解 setTimeout 的运行机制。

解答

一个例子

console.log(1);
setTimeout(function () {
    console.log(2);
}, 0);
console.log(3);

输出结果是:1 3 2

无论 setTimeout 的延迟时间是 0 还是 1000,结果都是先输出 3 后输出 2。这涉及到 JavaScript 的运行机制。

JavaScript 单线程

JavaScript 引擎是单线程执行的,浏览器中只有一个 JS 线程在运行程序(主线程)。所有任务分为两种:

同步任务:在主线程上排队执行,前一个任务执行完才能执行下一个。

异步任务:不进入主线程,而是进入任务队列(task queue)。只有主线程空闲时,才会从任务队列中取出异步任务执行。

运行机制

  1. 所有同步任务在主线程上执行,形成执行栈(Call Stack)
  2. 异步任务有了运行结果后,在任务队列中放置一个事件
  3. 执行栈中的同步任务执行完毕后,系统读取任务队列,将对应的异步任务放入执行栈开始执行
  4. 主线程不断重复第 3 步(Event Loop)

setTimeout 的执行时机

setTimeout 将指定的代码移出本次执行,等到下一轮 Event Loop 时检查是否到了指定时间。如果到了就执行,否则继续等待。

这意味着 setTimeout 指定的代码必须等到本次执行的所有同步代码都执行完才会执行

关键点

  • JavaScript 是单线程执行,通过事件循环实现异步
  • setTimeout 是异步任务,会被放入任务队列而不是立即执行
  • 即使延迟时间设为 0,也要等当前执行栈的同步任务全部完成
  • 主线程会不断循环检查任务队列,这就是 Event Loop