Event Loop事件循环

发布时间:2026/7/21 15:58:16
Event Loop事件循环 1. 同步和异步JavaScript是一种单线程语言所有任务都在一个线程上完成。一旦遇到大量任务或者遇到一个耗时的任务比如加载一个高清图片网页就会出现假死因为JavaScript停不下来也就无法响应用户的行为。为了防止主线程的阻塞JavaScript 有了 同步 和 异步 的概念。同步:调用一旦开始调用者必须等待方法执行完成才能继续执行后续方法。异步:方法一旦开始立即返回调用者无需等待其中方法执行完成就可以继续执行后续方法。2. 事件循环机制 Event Loop事件循环Event Loop是 JavaScript 运行时浏览器/Node.js用于协调同步代码、异步回调、页面渲染的一套调度机制。它决定了代码执行的顺序和时机2.1 执行顺序先执行一个宏任务也就是整体script里的代码遇到同步任务立即执行遇到宏任务则将宏任务放入宏任务队列中遇到微任务则将微任务放入微任务队列中当所有同步代码执行完毕后再将微任务从队列中调入主线程执行直至微任务队列清空哪怕执行微任务时又产生了新的微任务也会在本轮继续清空直到微任务队列为空才会取下一个宏任务。检查是否需要渲染页面如果需要渲染 → 执行渲染流程样式计算 → 布局 → 绘制如果不需要 → 跳过再从宏任务队列中取出一个执行一直循环直至所有任务执行完毕2.2 宏任务宏任务来源说明示例整个script标签的执行事件循环启动后取出的第一个宏任务所有全局同步代码都是它的执行体页面加载时执行的JS代码setTimeout/setInterval回调定时器延迟时间达到后回调被加入宏任务队列setTimeout((){}, 0)UI交互事件回调用户操作触发后回调加入宏任务队列click/keydown/mousemove/touchstart等I/O完成回调浏览器底层I/O操作完成后触发的回调注意不是JS回调本身fetch/XHR网络请求完成、FileReader文件读取完成、IndexedDB操作完成postMessage回调跨窗口/跨Worker通信时消息被接收后触发的回调window.postMessage(data)的onmessage回调MessageChannel回调通过MessageChannel创建的端口发送消息后接收端的onmessage回调const channel new MessageChannel(); channel.port1.onmessage () {}requestAnimationFrame回调浏览器下一次渲染前执行的回调属于宏任务但优先级高于普通宏任务做动画时常用的APIrequestIdleCallback回调浏览器空闲时期执行的回调优先级低于普通宏任务只有主线程无任务时才会执行执行低优先级任务如日志上报、预加载2.2 微任务微任务来源说明你熟悉的场景Promise.prototype.then/catch/finally回调最常见的微任务Promise状态变更后触发你写异步逻辑最常用的APIawait后面的代码await xx等价于 Promise.resolve(xx).then((){})await后的代码就是then的回调属于微任务await后面的语句不会阻塞同步代码queueMicrotask回调ES2020新增的API专门用于创建微任务比Promise.then更纯粹没有Promise状态转换的开销Vue3的nextTick底层就用它兜底MutationObserver回调监听DOM变化触发的回调DOM变更后、下一个宏任务前执行Vue2的nextTick在兼容环境下用它实现IntersectionObserver回调监听元素可见性变化触发的回调属于微任务图片懒加载、曝光埋点的核心API2.4 例题setTimeout(function(){ console.log(1); }); new Promise(function(resolve){ console.log(2); resolve(); }).then(function(){ console.log(3); }); console.log(4); //输出顺序2 4 3 1解释遇到setTimout异步宏任务放入宏任务队列中遇到new PromisePromise在实例化的过程中所执行的代码都是同步进行的所以输出2而Promise.then中注册的回调才是异步执行的将其放入微任务队列中遇到同步任务console.log(‘4’);输出4主线程中同步任务执行完从微任务队列中取出任务到主线程中输出3微任务队列为空从宏任务队列中取出任务到主线程中输出1宏任务队列为空结束setTimeout((){ new Promise(resolve { resolve(); }).then((){ console.log(test); }); console.log(4); }); new Promise(resolve { resolve(); console.log(1) }).then( () { console.log(3); Promise.resolve().then(() { console.log(before timeout); }).then(() { Promise.resolve().then(() { console.log(also before timeout) }) }) }) console.log(2); //输出1 2 3 before timeout also before timeout 4 test解释遇到setTimeout异步宏任务将() {console.log(4)}放入宏任务队列中遇到new PromisePromise在实例化的过程中所执行的代码都是同步进行的所以输出1而Promise.then中注册的回调才是异步执行的将其放入微任务队列中遇到同步任务console.log(2)输出2主线程中同步任务执行完从微任务队列中取出任务到主线程中输出3此微任务中又有微任务Promise.resolve().then(微任务a).then(微任务b)将其依次放入微任务队列中从微任务队列中取出任务a到主线程中输出 before timeout从微任务队列中取出任务b到主线程中任务b又注册了一个微任务c放入微任务队列中从微任务队列中取出任务c到主线程中输出 also before timeout微任务队列为空从宏任务队列中取出任务到主线程此任务中注册了一个微任务d将其放入微任务队列中接下来遇到输出4宏任务队列为空从微任务队列中取出任务d到主线程 输出test微任务队列为空结束