JavaScript 的事件循环机制是其单线程执行模型的核心。以下是事件循环的详细步骤:

事件循环的步骤

  1. 所有同步任务都在主线程上执行,形成一个执行栈(Execution Context Stack)

    • JavaScript 中所有的代码都是在一个单一的主线程上执行的。
    • 同步任务会按照顺序依次压入执行栈中,执行完毕后依次弹出。
  2. 主线程之外,还存在一个任务队列(Task Queue)

    • 任务队列用于存放所有需要在未来某个时刻执行的异步任务。
    • 任务队列分为两种:宏任务队列(Macro Task Queue)和微任务队列(Micro Task Queue)。
  3. 当主线程上的所有同步任务执行完毕后,主线程会清空执行栈

    • 同步任务执行完毕后,执行栈会被清空,表示当前的同步代码已经执行完毕。
  4. 然后读取任务队列中的第一个任务

    • 事件循环会从宏任务队列中读取第一个任务,并将其放入执行栈中执行。
  5. 每一个宏任务执行完毕后,都要清空所有的微任务

    • 在每个宏任务结束后,事件循环会检查微任务队列,并按照顺序执行所有微任务。
    • 微任务执行完毕后,事件循环才会继续处理下一个宏任务。
  6. 重复上述过程

    • 事件循环不断重复上述过程,确保异步任务被及时处理,同时主线程保持畅通。

具体流程

  1. 全局代码执行

    • 全局代码会被当作一个宏任务,压入执行栈中。
    • 在执行全局代码的过程中,如果遇到异步任务(例如 setTimeoutPromise 等),会将其回调函数放入对应的任务队列中。
  2. 事件循环开始

    • 全局代码执行完毕后,事件循环开始。
    • 从宏任务队列中取出第一个宏任务,执行它。
  3. 微任务执行

    • 当前宏任务执行完毕后,事件循环会检查微任务队列,并执行所有微任务。
  4. 下一个宏任务执行

    • 微任务执行完毕后,事件循环会从宏任务队列中取出下一个宏任务,继续执行。

示例代码

console.log('script start');

setTimeout(function() {
    console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
    console.log('promise1');
}).then(function() {
    console.log('promise2');
});

console.log('script end');

执行过程:

  1. 同步任务执行

    • 输出 script start
    • setTimeout 回调放入宏任务队列。
    • Promise 第一个 .then 回调放入微任务队列。
    • Promise 第二个 .then 回调放入微任务队列。
    • 输出 script end
  2. 全局代码执行完毕

    • 执行栈清空。
  3. 执行微任务

    • 执行第一个微任务,输出 promise1
    • 执行第二个微任务,输出 promise2
  4. 执行宏任务

    • 执行 setTimeout 回调,输出 setTimeout

最终输出顺序:

script start
script end
promise1
promise2
setTimeout

总结

  • 执行栈(Execution Context Stack):所有同步任务都在主线程上执行,形成一个执行栈。
  • 任务队列(Task Queue):包含宏任务队列和微任务队列,用于存放异步任务的回调。
  • 事件循环(Event Loop):负责在执行栈为空时,从任务队列中取出任务执行,确保异步任务被及时处理。

SuRuiGit
264 声望23 粉丝

« 上一篇
webkit渲染原理