JavaScript 事件循环
事件循环(Event Loop)是 JavaScript 处理异步任务的核心机制,它负责协调 同步代码、异步回调、微任务、宏任务 的执行顺序。
确保 JavaScript 在单线程环境下仍能搞笑的处理异步操作,如 定时器、AJAX、Promise、事件监听 等。
TIP
JavaScript 是单线程的。它所能进行异步操作依赖于浏览器的其他线程,与其本身并无直接关系。
栗子:
当进行AJAX操作时,JS 会将它交给浏览器的 AJAX线程,在执行结束后,回调函数会被放入 任务队列 中,待JS线程空闲时,再从队列中取出回调函数并执行。
模型涉及的容器
- 源代码:存放源函数;
- 调用栈:取得函数,在此处进行执行;
- 异步线程(Ajax线程、定时器线程等):存放异步操作,结束后会将回调放入任务队列中;
- 任务队列:用于存放异步任务的回调,分为 微任务队列 和 宏任务队列
- 微任务队列:只有一个,存放微任务
- promise.then()、promise.catch()
- new MutaionObserver()
- process.nextTick()
- 宏任务队列:可能存在多个,存放宏任务,如 setTimeout、setInterval、DOM 事件等;
- setTimeout()
- setInterval()
- setImmediate()
- 微任务队列:只有一个,存放微任务
- 事件循环:不断检查调用栈是否为空,如果为空,就会执行任务队列中的任务(优先执行微任务);
事件循环执行顺序
1.从 源代码 中拿取函数,如果是 同步函数 ,直接执行,如果是 异步函数 则放入对应的异步线程中;
2.在调用栈中执行同步代码,执行完成后出栈;
3.异步函数执行结束后将回调放入任务队列中,任务队列分为 微任务队列 和 宏任务队列;
4.循环检查如果调用栈为空,则从 任务队列 中取出任务,放入 调用栈 中执行,遵循 微任务优先 的原则
举个栗子
js
console.log('同步代码1');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('同步代码2');
}).then(() => {
console.log('promise');
});
console.log('同步代码3');
// 执行结果:
// 同步代码1
// 同步代码2
// 同步代码3
// promise
// setTimeout
执行结果分析
在代码中由 同步代码 和 异步代码 组成。
其中 setTimeout()
在执行结束后会被放入 宏队列,而 Promise.resolve().then()
会被放入 微任务队列 中。
在执行时,首先执行 同步代码,因此最先输出:同步代码1、同步代码2、同步代码3。
同步代码执行结束后,调用栈清空,这时会从任务队列中拿取函数,因为优先拿取 微任务,因此输出:promise
调用栈再次清空后,再次从 任务队列 中拿取函数,此时没有 微任务,因此拿去 宏任务,输出:setTimeout
总结
- 事件循环负责调度 同步任务、微任务和宏任务的执行顺序。
- 同步代码限制性,微任务优先于宏任务。
async/await
是Promise
的语法糖,await
后代码会进入微任务。