[{"data":1,"prerenderedAt":1448},["ShallowReactive",2],{"blog-js-event-loop":3},{"id":4,"title":5,"body":6,"category":1287,"date":1435,"description":1436,"draft":1437,"extension":1438,"meta":1439,"navigation":409,"path":1440,"seo":1441,"stem":1442,"tags":1443,"__hash__":1447},"blog\u002Fblog\u002Fjs-event-loop.md","浏览器事件循环：从进程模型到任务队列",{"type":7,"value":8,"toc":1417},"minimark",[9,13,26,29,33,38,45,52,56,67,73,77,83,89,107,111,114,133,136,143,149,173,183,193,197,204,232,235,241,252,258,264,268,283,287,294,542,557,560,1161,1165,1175,1184,1203,1212,1215,1282,1285,1340,1343,1347,1358,1362,1368,1382,1385,1414],[10,11,5],"h1",{"id":12},"浏览器事件循环从进程模型到任务队列",[14,15,16,17,21,22,25],"p",{},"JavaScript 常被说成「单线程」，但真正决定页面是否流畅的，是浏览器里那条繁忙的 ",[18,19,20],"strong",{},"渲染主线程","。它既要解析 HTML\u002FCSS、布局绘制，又要执行 JS 与事件回调——这些任务如何排队、谁先谁后，就是 ",[18,23,24],{},"事件循环（消息循环）"," 要回答的问题。",[14,27,28],{},"下文从浏览器进程模型讲起，再到异步模型、阻塞渲染的实测，以及 W3C 与 Chrome 对任务队列的最新理解。",[30,31,32],"h2",{"id":32},"浏览器的进程与线程",[34,35,37],"h3",{"id":36},"进程是什么","进程是什么？",[14,39,40,41,44],{},"程序运行需要专属的内存空间，这块空间可以理解为 ",[18,42,43],{},"进程","。每个应用至少有一个进程；进程之间相互独立，若要通信也需要双方同意。",[14,46,47],{},[48,49],"img",{"alt":50,"src":51},"进程与内存空间示意图","\u002Fdemos\u002Fevent-loop\u002Fprocess.png",[34,53,55],{"id":54},"线程是什么","线程是什么？",[14,57,58,59,62,63,66],{},"有了进程，才能在其中运行代码。执行代码的单元叫 ",[18,60,61],{},"线程","。进程启动后会自动创建一个线程运行代码，称为 ",[18,64,65],{},"主线程","；若需并行执行多段代码，进程内还可再开更多线程。",[14,68,69],{},[48,70],{"alt":71,"src":72},"进程内的主线程与额外线程","\u002Fdemos\u002Fevent-loop\u002Fthread.png",[34,74,76],{"id":75},"浏览器有哪些进程","浏览器有哪些进程？",[14,78,79,82],{},[18,80,81],{},"浏览器是多进程、多线程应用。"," 启动后会拉起多个进程，以降低相互影响和连环崩溃的风险。可在浏览器任务管理器中查看当前进程列表。",[14,84,85],{},[48,86],{"alt":87,"src":88},"浏览器主要进程示意","\u002Fdemos\u002Fevent-loop\u002Fbrowser.png",[14,90,91,92,95,96,98,99,106],{},"与前端开发最相关的是 ",[18,93,94],{},"渲染进程","：启动后会开启 ",[18,97,20],{},"，负责执行 HTML、CSS、JavaScript。默认情况下，每个标签页对应一个渲染进程，标签页之间互不影响。（Chrome 的进程模型仍在演进，详见 ",[100,101,105],"a",{"href":102,"rel":103},"https:\u002F\u002Fchromium.googlesource.com\u002Fchromium\u002Fsrc\u002F+\u002Fmain\u002Fdocs\u002Fprocess_model_and_site_isolation.md#Modes-and-Availability",[104],"nofollow","Chrome 官方说明","。）",[30,108,110],{"id":109},"渲染主线程如何工作","渲染主线程如何工作？",[14,112,113],{},"渲染主线程是浏览器中最繁忙的线程，典型工作包括：",[115,116,117,121,124,127,130],"ul",{},[118,119,120],"li",{},"解析 HTML、CSS",[118,122,123],{},"计算样式、布局、处理图层",[118,125,126],{},"以约 60fps 重绘页面",[118,128,129],{},"执行全局 JS、事件处理函数、计时器回调",[118,131,132],{},"……",[14,134,135],{},"这么多任务如何调度？例如：JS 执行到一半时用户点了按钮，要立即处理点击吗？计时器到时了，要立即跑回调吗？点击与计时器同时到来，先处理谁？",[14,137,138,139,142],{},"主线程的做法是：",[18,140,141],{},"排队","。",[14,144,145],{},[48,146],{"alt":147,"src":148},"事件循环与消息队列","\u002Fdemos\u002Fevent-loop\u002Fqueue.png",[150,151,152,159,166],"ol",{},[118,153,154,155,158],{},"渲染主线程进入一个 ",[18,156,157],{},"不会结束的循环","（事件循环）。",[118,160,161,162,165],{},"每次循环检查 ",[18,163,164],{},"消息队列","：有任务则取出第一个执行，执行完再进入下一轮；没有任务则休眠。",[118,167,168,169,172],{},"其他线程（含其他进程的线程）可随时把任务加到队列 ",[18,170,171],{},"末尾","；若主线程在休眠，会被唤醒继续取任务。",[14,174,175,176,179,180,142],{},"整条链路也叫 ",[18,177,178],{},"事件循环"," 或 ",[18,181,182],{},"消息循环",[184,185,186],"blockquote",{},[14,187,188,189,192],{},"思考题：为什么渲染进程不用多个线程并行做解析、布局、绘制？",[190,191],"br",{},"\n简答：DOM 与样式状态需要一致的可预测顺序；多线程并行改同一棵 DOM 树会带来同步与竞态难题，单主线程 + 队列调度是更稳妥的工程取舍（GPU 合成等仍可由其他线程辅助）。",[30,194,196],{"id":195},"异步主线程不能阻塞","异步：主线程不能阻塞",[14,198,199,200,203],{},"有些任务 ",[18,201,202],{},"无法立刻完成","，例如：",[115,205,206,217,226],{},[118,207,208,209,213,214],{},"计时器：",[210,211,212],"code",{},"setTimeout","、",[210,215,216],{},"setInterval",[118,218,219,220,213,223],{},"网络：",[210,221,222],{},"XHR",[210,224,225],{},"Fetch",[118,227,228,229],{},"用户操作：",[210,230,231],{},"addEventListener",[14,233,234],{},"若主线程一直等到「时间到了」「请求返回」「用户点击」，就会长期阻塞，页面卡死：",[14,236,237],{},[48,238],{"alt":239,"src":240},"同步等待导致主线程阻塞","\u002Fdemos\u002Fevent-loop\u002Fsync.png",[14,242,243,244,247,248,251],{},"渲染主线程承担页面更新与交互，",[18,245,246],{},"绝不能被长时间阻塞","。浏览器因此采用 ",[18,249,250],{},"异步","：",[14,253,254],{},[48,255],{"alt":256,"src":257},"异步模型：其他线程处理，回调入队","\u002Fdemos\u002Fevent-loop\u002Fasync.png",[14,259,260,261],{},"主线程把计时、网络、监听等交给其他线程，自己继续执行后续代码；待时机成熟，将回调包装成任务加入消息队列末尾，由主线程在空闲时调度。",[18,262,263],{},"异步模式下，主线程不会因等待 I\u002FO 而空转阻塞。",[34,265,267],{"id":266},"如何理解-js-的异步面试要点","如何理解 JS 的异步？（面试要点）",[115,269,270,277,280],{},[118,271,272,273,276],{},"JS 运行在渲染主线程中，",[18,274,275],{},"同一时刻只有一条执行栈","，故常被称为单线程语言。",[118,278,279],{},"主线程还要负责渲染；若大量任务同步等待，队列里其他任务无法执行，页面无法及时更新。",[118,281,282],{},"异步做法：计时、网络、事件等交由其他线程；完成后把回调入队，主线程按序执行。",[30,284,286],{"id":285},"js-为何会阻碍渲染","JS 为何会阻碍渲染？",[14,288,289,290,293],{},"改 DOM 与真正画到屏幕上并非同一瞬间完成。若主线程正在执行一段 ",[18,291,292],{},"长时间占用的 JS","，渲染任务只能等在队列后面。",[295,296,299],"code-block",{"lang":297,"title":298},"html","主线程阻塞导致画面延迟更新",[300,301,305],"pre",{"className":302,"code":303,"language":297,"meta":304,"style":304},"language-html shiki shiki-themes github-light github-dark","\u003Ch1>Hello, Event Loop!\u003C\u002Fh1>\n\u003Cbutton>change\u003C\u002Fbutton>\n\u003Cscript>\n  const h1 = document.querySelector('h1')\n  const btn = document.querySelector('button')\n\n  function delay(duration) {\n    const start = Date.now()\n    while (Date.now() - start \u003C duration) {}\n  }\n\n  btn.onclick = function () {\n    h1.textContent = '页面已更新！'\n    delay(3000)\n  }\n\u003C\u002Fscript>\n","",[210,306,307,327,342,352,383,404,411,429,449,474,480,485,502,514,527,532],{"__ignoreMap":304},[308,309,312,316,319,322,324],"span",{"class":310,"line":311},"line",1,[308,313,315],{"class":314},"sVt8B","\u003C",[308,317,10],{"class":318},"s9eBZ",[308,320,321],{"class":314},">Hello, Event Loop!\u003C\u002F",[308,323,10],{"class":318},[308,325,326],{"class":314},">\n",[308,328,330,332,335,338,340],{"class":310,"line":329},2,[308,331,315],{"class":314},[308,333,334],{"class":318},"button",[308,336,337],{"class":314},">change\u003C\u002F",[308,339,334],{"class":318},[308,341,326],{"class":314},[308,343,345,347,350],{"class":310,"line":344},3,[308,346,315],{"class":314},[308,348,349],{"class":318},"script",[308,351,326],{"class":314},[308,353,355,359,363,366,369,373,376,380],{"class":310,"line":354},4,[308,356,358],{"class":357},"szBVR","  const",[308,360,362],{"class":361},"sj4cs"," h1",[308,364,365],{"class":357}," =",[308,367,368],{"class":314}," document.",[308,370,372],{"class":371},"sScJk","querySelector",[308,374,375],{"class":314},"(",[308,377,379],{"class":378},"sZZnC","'h1'",[308,381,382],{"class":314},")\n",[308,384,386,388,391,393,395,397,399,402],{"class":310,"line":385},5,[308,387,358],{"class":357},[308,389,390],{"class":361}," btn",[308,392,365],{"class":357},[308,394,368],{"class":314},[308,396,372],{"class":371},[308,398,375],{"class":314},[308,400,401],{"class":378},"'button'",[308,403,382],{"class":314},[308,405,407],{"class":310,"line":406},6,[308,408,410],{"emptyLinePlaceholder":409},true,"\n",[308,412,414,417,420,422,426],{"class":310,"line":413},7,[308,415,416],{"class":357},"  function",[308,418,419],{"class":371}," delay",[308,421,375],{"class":314},[308,423,425],{"class":424},"s4XuR","duration",[308,427,428],{"class":314},") {\n",[308,430,432,435,438,440,443,446],{"class":310,"line":431},8,[308,433,434],{"class":357},"    const",[308,436,437],{"class":361}," start",[308,439,365],{"class":357},[308,441,442],{"class":314}," Date.",[308,444,445],{"class":371},"now",[308,447,448],{"class":314},"()\n",[308,450,452,455,458,460,463,466,469,471],{"class":310,"line":451},9,[308,453,454],{"class":357},"    while",[308,456,457],{"class":314}," (Date.",[308,459,445],{"class":371},[308,461,462],{"class":314},"() ",[308,464,465],{"class":357},"-",[308,467,468],{"class":314}," start ",[308,470,315],{"class":357},[308,472,473],{"class":314}," duration) {}\n",[308,475,477],{"class":310,"line":476},10,[308,478,479],{"class":314},"  }\n",[308,481,483],{"class":310,"line":482},11,[308,484,410],{"emptyLinePlaceholder":409},[308,486,488,491,494,496,499],{"class":310,"line":487},12,[308,489,490],{"class":314},"  btn.",[308,492,493],{"class":371},"onclick",[308,495,365],{"class":357},[308,497,498],{"class":357}," function",[308,500,501],{"class":314}," () {\n",[308,503,505,508,511],{"class":310,"line":504},13,[308,506,507],{"class":314},"    h1.textContent ",[308,509,510],{"class":357},"=",[308,512,513],{"class":378}," '页面已更新！'\n",[308,515,517,520,522,525],{"class":310,"line":516},14,[308,518,519],{"class":371},"    delay",[308,521,375],{"class":314},[308,523,524],{"class":361},"3000",[308,526,382],{"class":314},[308,528,530],{"class":310,"line":529},15,[308,531,479],{"class":314},[308,533,535,538,540],{"class":310,"line":534},16,[308,536,537],{"class":314},"\u003C\u002F",[308,539,349],{"class":318},[308,541,326],{"class":314},[14,543,544,545,548,549,556],{},"点击按钮后：",[210,546,547],{},"textContent"," 虽已修改，但 ",[18,550,551,552,555],{},"画面要等 ",[210,553,554],{},"delay(3000)"," 跑完"," 才会更新——因为渲染步骤也要主线程空闲才能执行。",[14,558,559],{},"下方可亲自点击体验（约 3 秒内标题不会变，之后才刷新）：",[561,562,564],"code-playground",{"title":563},"JS 阻塞渲染",[300,565,569],{"className":566,"code":567,"language":568,"meta":304,"style":304},"language-vue shiki shiki-themes github-light github-dark","\u003Ctemplate>\n  \u003Cdiv class=\"demo\">\n    \u003Ch1>{{ title }}\u003C\u002Fh1>\n    \u003Cp class=\"hint\">点击按钮后，标题会延迟约 3 秒才变化。\u003C\u002Fp>\n    \u003Cbutton class=\"btn\" @click=\"onChange\">change\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref } from 'vue'\n\nconst title = ref('Hello, Event Loop!')\n\nfunction delay(duration) {\n  const start = Date.now()\n  while (Date.now() - start \u003C duration) {}\n}\n\nfunction onChange() {\n  title.value = '页面已更新！'\n  delay(3000)\n}\n\u003C\u002Fscript>\n\n\u003Cstyle scoped>\n.demo {\n  padding: 24px;\n  font-family: system-ui, sans-serif;\n  text-align: center;\n}\n\nh1 {\n  margin: 0 0 12px;\n  font-size: 1.5rem;\n}\n\n.hint {\n  margin: 0 0 16px;\n  color: #64748b;\n  font-size: 0.9rem;\n}\n\n.btn {\n  padding: 10px 20px;\n  border: none;\n  border-radius: 8px;\n  background: #6366f1;\n  color: white;\n  font-weight: 600;\n  cursor: pointer;\n}\n\u003C\u002Fstyle>\n","vue",[210,570,571,580,598,612,632,659,668,676,680,691,705,709,729,733,746,760,779,785,790,801,811,823,828,837,842,855,864,882,901,914,919,924,931,952,968,973,978,986,1004,1017,1031,1036,1041,1049,1068,1081,1096,1109,1121,1134,1147,1152],{"__ignoreMap":304},[308,572,573,575,578],{"class":310,"line":311},[308,574,315],{"class":314},[308,576,577],{"class":318},"template",[308,579,326],{"class":314},[308,581,582,585,588,591,593,596],{"class":310,"line":329},[308,583,584],{"class":314},"  \u003C",[308,586,587],{"class":318},"div",[308,589,590],{"class":371}," class",[308,592,510],{"class":314},[308,594,595],{"class":378},"\"demo\"",[308,597,326],{"class":314},[308,599,600,603,605,608,610],{"class":310,"line":344},[308,601,602],{"class":314},"    \u003C",[308,604,10],{"class":318},[308,606,607],{"class":314},">{{ title }}\u003C\u002F",[308,609,10],{"class":318},[308,611,326],{"class":314},[308,613,614,616,618,620,622,625,628,630],{"class":310,"line":354},[308,615,602],{"class":314},[308,617,14],{"class":318},[308,619,590],{"class":371},[308,621,510],{"class":314},[308,623,624],{"class":378},"\"hint\"",[308,626,627],{"class":314},">点击按钮后，标题会延迟约 3 秒才变化。\u003C\u002F",[308,629,14],{"class":318},[308,631,326],{"class":314},[308,633,634,636,638,640,642,645,648,650,653,655,657],{"class":310,"line":385},[308,635,602],{"class":314},[308,637,334],{"class":318},[308,639,590],{"class":371},[308,641,510],{"class":314},[308,643,644],{"class":378},"\"btn\"",[308,646,647],{"class":371}," @click",[308,649,510],{"class":314},[308,651,652],{"class":378},"\"onChange\"",[308,654,337],{"class":314},[308,656,334],{"class":318},[308,658,326],{"class":314},[308,660,661,664,666],{"class":310,"line":406},[308,662,663],{"class":314},"  \u003C\u002F",[308,665,587],{"class":318},[308,667,326],{"class":314},[308,669,670,672,674],{"class":310,"line":413},[308,671,537],{"class":314},[308,673,577],{"class":318},[308,675,326],{"class":314},[308,677,678],{"class":310,"line":431},[308,679,410],{"emptyLinePlaceholder":409},[308,681,682,684,686,689],{"class":310,"line":451},[308,683,315],{"class":314},[308,685,349],{"class":318},[308,687,688],{"class":371}," setup",[308,690,326],{"class":314},[308,692,693,696,699,702],{"class":310,"line":476},[308,694,695],{"class":357},"import",[308,697,698],{"class":314}," { ref } ",[308,700,701],{"class":357},"from",[308,703,704],{"class":378}," 'vue'\n",[308,706,707],{"class":310,"line":482},[308,708,410],{"emptyLinePlaceholder":409},[308,710,711,714,717,719,722,724,727],{"class":310,"line":487},[308,712,713],{"class":357},"const",[308,715,716],{"class":361}," title",[308,718,365],{"class":357},[308,720,721],{"class":371}," ref",[308,723,375],{"class":314},[308,725,726],{"class":378},"'Hello, Event Loop!'",[308,728,382],{"class":314},[308,730,731],{"class":310,"line":504},[308,732,410],{"emptyLinePlaceholder":409},[308,734,735,738,740,742,744],{"class":310,"line":516},[308,736,737],{"class":357},"function",[308,739,419],{"class":371},[308,741,375],{"class":314},[308,743,425],{"class":424},[308,745,428],{"class":314},[308,747,748,750,752,754,756,758],{"class":310,"line":529},[308,749,358],{"class":357},[308,751,437],{"class":361},[308,753,365],{"class":357},[308,755,442],{"class":314},[308,757,445],{"class":371},[308,759,448],{"class":314},[308,761,762,765,767,769,771,773,775,777],{"class":310,"line":534},[308,763,764],{"class":357},"  while",[308,766,457],{"class":314},[308,768,445],{"class":371},[308,770,462],{"class":314},[308,772,465],{"class":357},[308,774,468],{"class":314},[308,776,315],{"class":357},[308,778,473],{"class":314},[308,780,782],{"class":310,"line":781},17,[308,783,784],{"class":314},"}\n",[308,786,788],{"class":310,"line":787},18,[308,789,410],{"emptyLinePlaceholder":409},[308,791,793,795,798],{"class":310,"line":792},19,[308,794,737],{"class":357},[308,796,797],{"class":371}," onChange",[308,799,800],{"class":314},"() {\n",[308,802,804,807,809],{"class":310,"line":803},20,[308,805,806],{"class":314},"  title.value ",[308,808,510],{"class":357},[308,810,513],{"class":378},[308,812,814,817,819,821],{"class":310,"line":813},21,[308,815,816],{"class":371},"  delay",[308,818,375],{"class":314},[308,820,524],{"class":361},[308,822,382],{"class":314},[308,824,826],{"class":310,"line":825},22,[308,827,784],{"class":314},[308,829,831,833,835],{"class":310,"line":830},23,[308,832,537],{"class":314},[308,834,349],{"class":318},[308,836,326],{"class":314},[308,838,840],{"class":310,"line":839},24,[308,841,410],{"emptyLinePlaceholder":409},[308,843,845,847,850,853],{"class":310,"line":844},25,[308,846,315],{"class":314},[308,848,849],{"class":318},"style",[308,851,852],{"class":371}," scoped",[308,854,326],{"class":314},[308,856,858,861],{"class":310,"line":857},26,[308,859,860],{"class":371},".demo",[308,862,863],{"class":314}," {\n",[308,865,867,870,873,876,879],{"class":310,"line":866},27,[308,868,869],{"class":361},"  padding",[308,871,872],{"class":314},": ",[308,874,875],{"class":361},"24",[308,877,878],{"class":357},"px",[308,880,881],{"class":314},";\n",[308,883,885,888,890,893,896,899],{"class":310,"line":884},28,[308,886,887],{"class":361},"  font-family",[308,889,872],{"class":314},[308,891,892],{"class":361},"system-ui",[308,894,895],{"class":314},", ",[308,897,898],{"class":361},"sans-serif",[308,900,881],{"class":314},[308,902,904,907,909,912],{"class":310,"line":903},29,[308,905,906],{"class":361},"  text-align",[308,908,872],{"class":314},[308,910,911],{"class":361},"center",[308,913,881],{"class":314},[308,915,917],{"class":310,"line":916},30,[308,918,784],{"class":314},[308,920,922],{"class":310,"line":921},31,[308,923,410],{"emptyLinePlaceholder":409},[308,925,927,929],{"class":310,"line":926},32,[308,928,10],{"class":318},[308,930,863],{"class":314},[308,932,934,937,939,942,945,948,950],{"class":310,"line":933},33,[308,935,936],{"class":361},"  margin",[308,938,872],{"class":314},[308,940,941],{"class":361},"0",[308,943,944],{"class":361}," 0",[308,946,947],{"class":361}," 12",[308,949,878],{"class":357},[308,951,881],{"class":314},[308,953,955,958,960,963,966],{"class":310,"line":954},34,[308,956,957],{"class":361},"  font-size",[308,959,872],{"class":314},[308,961,962],{"class":361},"1.5",[308,964,965],{"class":357},"rem",[308,967,881],{"class":314},[308,969,971],{"class":310,"line":970},35,[308,972,784],{"class":314},[308,974,976],{"class":310,"line":975},36,[308,977,410],{"emptyLinePlaceholder":409},[308,979,981,984],{"class":310,"line":980},37,[308,982,983],{"class":371},".hint",[308,985,863],{"class":314},[308,987,989,991,993,995,997,1000,1002],{"class":310,"line":988},38,[308,990,936],{"class":361},[308,992,872],{"class":314},[308,994,941],{"class":361},[308,996,944],{"class":361},[308,998,999],{"class":361}," 16",[308,1001,878],{"class":357},[308,1003,881],{"class":314},[308,1005,1007,1010,1012,1015],{"class":310,"line":1006},39,[308,1008,1009],{"class":361},"  color",[308,1011,872],{"class":314},[308,1013,1014],{"class":361},"#64748b",[308,1016,881],{"class":314},[308,1018,1020,1022,1024,1027,1029],{"class":310,"line":1019},40,[308,1021,957],{"class":361},[308,1023,872],{"class":314},[308,1025,1026],{"class":361},"0.9",[308,1028,965],{"class":357},[308,1030,881],{"class":314},[308,1032,1034],{"class":310,"line":1033},41,[308,1035,784],{"class":314},[308,1037,1039],{"class":310,"line":1038},42,[308,1040,410],{"emptyLinePlaceholder":409},[308,1042,1044,1047],{"class":310,"line":1043},43,[308,1045,1046],{"class":371},".btn",[308,1048,863],{"class":314},[308,1050,1052,1054,1056,1059,1061,1064,1066],{"class":310,"line":1051},44,[308,1053,869],{"class":361},[308,1055,872],{"class":314},[308,1057,1058],{"class":361},"10",[308,1060,878],{"class":357},[308,1062,1063],{"class":361}," 20",[308,1065,878],{"class":357},[308,1067,881],{"class":314},[308,1069,1071,1074,1076,1079],{"class":310,"line":1070},45,[308,1072,1073],{"class":361},"  border",[308,1075,872],{"class":314},[308,1077,1078],{"class":361},"none",[308,1080,881],{"class":314},[308,1082,1084,1087,1089,1092,1094],{"class":310,"line":1083},46,[308,1085,1086],{"class":361},"  border-radius",[308,1088,872],{"class":314},[308,1090,1091],{"class":361},"8",[308,1093,878],{"class":357},[308,1095,881],{"class":314},[308,1097,1099,1102,1104,1107],{"class":310,"line":1098},47,[308,1100,1101],{"class":361},"  background",[308,1103,872],{"class":314},[308,1105,1106],{"class":361},"#6366f1",[308,1108,881],{"class":314},[308,1110,1112,1114,1116,1119],{"class":310,"line":1111},48,[308,1113,1009],{"class":361},[308,1115,872],{"class":314},[308,1117,1118],{"class":361},"white",[308,1120,881],{"class":314},[308,1122,1124,1127,1129,1132],{"class":310,"line":1123},49,[308,1125,1126],{"class":361},"  font-weight",[308,1128,872],{"class":314},[308,1130,1131],{"class":361},"600",[308,1133,881],{"class":314},[308,1135,1137,1140,1142,1145],{"class":310,"line":1136},50,[308,1138,1139],{"class":361},"  cursor",[308,1141,872],{"class":314},[308,1143,1144],{"class":361},"pointer",[308,1146,881],{"class":314},[308,1148,1150],{"class":310,"line":1149},51,[308,1151,784],{"class":314},[308,1153,1155,1157,1159],{"class":310,"line":1154},52,[308,1156,537],{"class":314},[308,1158,849],{"class":318},[308,1160,326],{"class":314},[30,1162,1164],{"id":1163},"任务有优先级吗","任务有优先级吗？",[14,1166,1167,1170,1171,1174],{},[18,1168,1169],{},"单个任务","在所属队列里通常是先进先出，没有「插队」；但 ",[18,1172,1173],{},"队列之间"," 有优先级差异。",[14,1176,1177,1178,1183],{},"W3C 现行表述要点（",[100,1179,1182],{"href":1180,"rel":1181},"https:\u002F\u002Fhtml.spec.whatwg.org\u002Fmultipage\u002Fwebappapis.html#perform-a-microtask-checkpoint",[104],"Perform a microtask checkpoint","）：",[115,1185,1186,1193,1196],{},[118,1187,1188,1189,1192],{},"每个任务有 ",[18,1190,1191],{},"任务类型","；同类型任务在同一队列，不同类型可分到不同队列。",[118,1194,1195],{},"一次事件循环中，浏览器可按情况从不同队列取任务。",[118,1197,1198,1199,1202],{},"浏览器必须维护 ",[18,1200,1201],{},"微队列（microtask queue）","，其中任务优先于其他队列执行。",[184,1204,1205],{},[14,1206,1207,1208,1211],{},"随着浏览器复杂度提升，简单区分「宏队列 \u002F 微队列」已不足以描述全部行为，但 ",[18,1209,1210],{},"微任务优先清空"," 仍是硬性要求。",[14,1213,1214],{},"在 Chrome 实现中，至少包括：",[1216,1217,1218,1234],"table",{},[1219,1220,1221],"thead",{},[1222,1223,1224,1228,1231],"tr",{},[1225,1226,1227],"th",{},"队列",[1225,1229,1230],{},"典型内容",[1225,1232,1233],{},"优先级（相对）",[1235,1236,1237,1255,1266],"tbody",{},[1222,1238,1239,1243,1252],{},[1240,1241,1242],"td",{},"微队列",[1240,1244,1245,213,1248,1251],{},[210,1246,1247],{},"Promise.then",[210,1249,1250],{},"MutationObserver"," 等",[1240,1253,1254],{},"最高",[1222,1256,1257,1260,1263],{},[1240,1258,1259],{},"交互队列",[1240,1261,1262],{},"用户操作产生的事件处理",[1240,1264,1265],{},"高",[1222,1267,1268,1271,1279],{},[1240,1269,1270],{},"延时队列",[1240,1272,1273,1275,1276,1278],{},[210,1274,212],{}," \u002F ",[210,1277,216],{}," 到期回调",[1240,1280,1281],{},"中",[14,1283,1284],{},"立即把函数放进微队列的常见写法：",[295,1286,1289],{"lang":1287,"title":1288},"javascript","Promise 入微队列",[300,1290,1293],{"className":1291,"code":1292,"language":1287,"meta":304,"style":304},"language-javascript shiki shiki-themes github-light github-dark","Promise.resolve().then(() => {\n  console.log('microtask')\n})\n",[210,1294,1295,1320,1335],{"__ignoreMap":304},[308,1296,1297,1300,1303,1306,1309,1312,1315,1318],{"class":310,"line":311},[308,1298,1299],{"class":361},"Promise",[308,1301,1302],{"class":314},".",[308,1304,1305],{"class":371},"resolve",[308,1307,1308],{"class":314},"().",[308,1310,1311],{"class":371},"then",[308,1313,1314],{"class":314},"(() ",[308,1316,1317],{"class":357},"=>",[308,1319,863],{"class":314},[308,1321,1322,1325,1328,1330,1333],{"class":310,"line":329},[308,1323,1324],{"class":314},"  console.",[308,1326,1327],{"class":371},"log",[308,1329,375],{"class":314},[308,1331,1332],{"class":378},"'microtask'",[308,1334,382],{"class":314},[308,1336,1337],{"class":310,"line":344},[308,1338,1339],{"class":314},"})\n",[30,1341,1342],{"id":1342},"常见面试题",[34,1344,1346],{"id":1345},"阐述-js-的事件循环","阐述 JS 的事件循环",[14,1348,1349,1350,1353,1354,1357],{},"事件循环是渲染主线程的工作方式：开启不会结束的循环，每次从队列取一个任务执行；其他线程在合适时机把任务加到队列末尾。任务按类型分队列，浏览器在一次循环中决定取哪个队列；",[18,1351,1352],{},"微队列任务必须优先调度执行","。Chrome 源码层面可理解为 ",[210,1355,1356],{},"for"," 循环 + 队列取任务，而非过去教科书里单一的「宏队列」模型。",[34,1359,1361],{"id":1360},"计时器能精确计时吗","计时器能精确计时吗？",[14,1363,1364,1367],{},[18,1365,1366],{},"不能做到严格精确","，原因包括：",[150,1369,1370,1373,1376],{},[118,1371,1372],{},"硬件与 OS 计时本身存在误差，JS 计时器最终依赖系统调用。",[118,1374,1375],{},"W3C 规定：嵌套超过 5 层的计时器，最小间隔为 4ms，短于 4ms 的设定会被抬高。",[118,1377,1378,1379,142],{},"回调只在主线程空闲时执行，前面若有长任务，到期时间到了也会 ",[18,1380,1381],{},"晚跑",[30,1383,1384],{"id":1384},"小结",[115,1386,1387,1393,1398,1407],{},[118,1388,1389,1390,1392],{},"浏览器多进程隔离标签页；",[18,1391,20],{}," 是页面与 JS 的核心调度点。",[118,1394,1395,1397],{},[18,1396,178],{}," 用消息队列 + 无限循环保证任务有序执行。",[118,1399,1400,1402,1403,1406],{},[18,1401,250],{}," 避免主线程等待 I\u002FO；",[18,1404,1405],{},"长同步 JS"," 仍会阻塞渲染与交互。",[118,1408,1409,1410,1413],{},"理解 ",[18,1411,1412],{},"微队列优先"," 与多队列模型，比死记「宏\u002F微」更符合当前规范与 Chrome 行为。",[849,1415,1416],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":304,"searchDepth":344,"depth":344,"links":1418},[1419,1424,1425,1428,1429,1430,1434],{"id":32,"depth":329,"text":32,"children":1420},[1421,1422,1423],{"id":36,"depth":344,"text":37},{"id":54,"depth":344,"text":55},{"id":75,"depth":344,"text":76},{"id":109,"depth":329,"text":110},{"id":195,"depth":329,"text":196,"children":1426},[1427],{"id":266,"depth":344,"text":267},{"id":285,"depth":329,"text":286},{"id":1163,"depth":329,"text":1164},{"id":1342,"depth":329,"text":1342,"children":1431},[1432,1433],{"id":1345,"depth":344,"text":1346},{"id":1360,"depth":344,"text":1361},{"id":1384,"depth":329,"text":1384},"2026-06-24","梳理渲染主线程如何调度任务、异步为何必要、JS 为何会阻塞渲染，以及 Chrome 中延时\u002F交互\u002F微队列的优先级。",false,"md",{},"\u002Fblog\u002Fjs-event-loop",{"title":5,"description":1436},"blog\u002Fjs-event-loop",[1444,1445,1446],"browser","event-loop","interview","zpAwwSlMoh5xOkPrZCWcA8rP62VybczZfOtCHemEflo",1782309644800]