JavaScript 的事件循环机制是其单线程执行模型的核心。以下是事件循环的详细步骤:
所有同步任务都在主线程上执行,形成一个执行栈(Execution Context Stack):
JavaScript 中所有的代码都是在一个单一的主线程上执行的。
同步任务会按照顺序依次压入执行栈中,执行完毕后依次弹出。
主线程之外,还存在一个任务队列(Task Queue):
任务队列用于存放所有需要在未来某个时刻执行的异步任务。
任务队列分为两种:宏任务队列(Macro Task Queue)和微任务队列(Micro Task Queue)。
当主线程上的所有同步任务执行完毕后,主线程会清空执行栈:
同步任务执行完毕后,执行栈会被清空,表示当前的同步代码已经执行完毕。
然后读取任务队列中的第一个任务:
事件循环会从宏任务队列中读取第一个任务,并将其放入执行栈中执行。
每一个宏任务执行完毕后,都要清空所有的微任务:
在每个宏任务结束后,事件循环会检查微任务队列,并按照顺序执行所有微任务。
微任务执行完毕后,事件循环才会继续处理下一个宏任务。
重复上述过程:
事件循环不断重复上述过程,确保异步任务被及时处理,同时主线程保持畅通。
全局代码执行:
全局代码会被当作一个宏任务,压入执行栈中。
在执行全局代码的过程中,如果遇到异步任务(例如 setTimeout
、Promise
等),会将其回调函数放入对应的任务队列中。
事件循环开始:
全局代码执行完毕后,事件循环开始。
从宏任务队列中取出第一个宏任务,执行它。
微任务执行:
当前宏任务执行完毕后,事件循环会检查微任务队列,并执行所有微任务。
下一个宏任务执行:
微任务执行完毕后,事件循环会从宏任务队列中取出下一个宏任务,继续执行。
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');
执行过程:
同步任务执行:
输出 script start
。
setTimeout
回调放入宏任务队列。
Promise
第一个 .then
回调放入微任务队列。
Promise
第二个 .then
回调放入微任务队列。
输出 script end
。
全局代码执行完毕:
执行栈清空。
执行微任务:
执行第一个微任务,输出 promise1
。
执行第二个微任务,输出 promise2
。
执行宏任务:
执行 setTimeout
回调,输出 setTimeout
。
最终输出顺序:
script start
script end
promise1
promise2
setTimeout
执行栈(Execution Context Stack):所有同步任务都在主线程上执行,形成一个执行栈。
任务队列(Task Queue):包含宏任务队列和微任务队列,用于存放异步任务的回调。
事件循环(Event Loop):负责在执行栈为空时,从任务队列中取出任务执行,确保异步任务被及时处理。
44 天前