最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JS的運(yùn)行機(jī)制之事件循環(huán)機(jī)制示例詳解

 更新時(shí)間:2025年07月21日 10:54:05   作者:小陳同學(xué)的前端基地  
事件循環(huán)是JavaScript實(shí)現(xiàn)異步編程的核心,通過單線程執(zhí)行模型協(xié)調(diào)同步代碼、回調(diào)隊(duì)列、異步事件及微任務(wù)和宏任務(wù),這篇文章主要介紹了JS運(yùn)行機(jī)制之事件循環(huán)機(jī)制的相關(guān)資料,需要的朋友可以參考下

前言

在現(xiàn)代Web開發(fā)中,JavaScript是構(gòu)建動態(tài)交互體驗(yàn)的核心。但它的運(yùn)行機(jī)制卻常被誤解——一個(gè)看似“單線程”的語言如何高效處理復(fù)雜的異步操作?JavaScript 是一門以事件驅(qū)動、單線程執(zhí)行為核心特征的編程語言。盡管它在執(zhí)行代碼時(shí)只有一個(gè)主線程,但卻能高效地處理各種異步任務(wù),如定時(shí)器、網(wǎng)絡(luò)請求、用戶交互等。這背后,正是強(qiáng)大的事件循環(huán)機(jī)制在默默運(yùn)轉(zhuǎn)。本文將從瀏覽器的進(jìn)程模型開始,逐步揭開 JavaScript 事件循環(huán)的神秘面紗。

一、進(jìn)程和線程

1.1進(jìn)程和線程的基本概念

要想了解JavaScript的運(yùn)行機(jī)制,首先得了解進(jìn)程和線程是什么

進(jìn)程(Process)是操作系統(tǒng)分配資源的基本單位

線程(Thread) 是程序執(zhí)行的最小單位,一個(gè)進(jìn)程中可以包含多個(gè)線程,共享內(nèi)存資源。

  一個(gè)應(yīng)用至少一個(gè)進(jìn)程,每個(gè)進(jìn)程之間相互獨(dú)立,進(jìn)程之間共享內(nèi)存空間。

  一個(gè)進(jìn)程至少一個(gè)線程,進(jìn)程開啟后會自動創(chuàng)建一個(gè)線程來運(yùn)行代碼,稱為主線程。要是程序需要同時(shí)執(zhí)行多塊代碼,就需要啟動動更多線程來執(zhí)行代碼,所以一個(gè)進(jìn)程可以包含多個(gè)進(jìn)程。

而JavaScript本身就是 運(yùn)行在瀏覽器中,而瀏覽器本身也是一個(gè)多進(jìn)程、多線程的復(fù)雜應(yīng)用

1.2 瀏覽器的多進(jìn)程架構(gòu)

現(xiàn)代瀏覽器(如 Chrome)采用多進(jìn)程架構(gòu),其主要進(jìn)程包括:

瀏覽器主進(jìn)程:負(fù)責(zé)地址欄、書簽欄、前進(jìn)后退、標(biāo)簽管理等 UI 操作。

網(wǎng)絡(luò)進(jìn)程:處理網(wǎng)絡(luò)資源的下載(如 HTML、CSS、JS、圖片等)。

渲染進(jìn)程:每打開一個(gè)標(biāo)簽頁,通常會分配一個(gè)獨(dú)立的渲染進(jìn)程,負(fù)責(zé)頁面渲染與 JavaScript 執(zhí)行。

GPU進(jìn)程:處理與圖形渲染相關(guān)的任務(wù),包括3D CSS效果、頁面UI的GPU加速繪制,以及視頻解碼等

 插件進(jìn)程(Plugin Process):獨(dú)立運(yùn)行瀏覽器插件(如Flash、廣告攔截工具),防止插件崩潰影響瀏覽器或其他頁面

輔助進(jìn)程(根據(jù)場景啟動):跨頁面共享的JavaScript線程,獨(dú)立于渲染進(jìn)程管理

瀏覽器插件種類繁多,但我們需要重點(diǎn)掌握J(rèn)avaScript的運(yùn)行機(jī)制。這就要從關(guān)鍵的渲染進(jìn)程入手,它與前端頁面渲染密切相關(guān)。

1.3 渲染進(jìn)程中的主線程

渲染進(jìn)程內(nèi)部,核心是渲染主線程,它負(fù)責(zé):

解析 HTML,構(gòu)建 DOM 樹

解析 CSS,構(gòu)建 CSSOM

執(zhí)行 JavaScript

計(jì)算樣式、布局、繪制

JavaScript 的運(yùn)行環(huán)境就在這個(gè)主線程中。因此,JavaScript 是單線程執(zhí)行。

二、異步機(jī)制

JavaScript 只運(yùn)行在瀏覽器的渲染主線程中,渲染主線程就只有一個(gè),而這個(gè)線程既要負(fù)責(zé)腳本執(zhí)行,又要承擔(dān)頁面渲染、事件響應(yīng)等工作。試想一下,如果我們讓 JS 主線程一直執(zhí)行某個(gè)任務(wù)(比如復(fù)雜的循環(huán)),那其他任務(wù)(如用戶點(diǎn)擊)就無法響應(yīng),頁面也無法渲染更新,用戶體驗(yàn)會極差。

2.1 同步的代價(jià)

我們先來分析一段代碼

while (true) {
  // 模擬高消耗運(yùn)算
}

這段代碼是一個(gè)死循環(huán),它將讓頁面徹底卡死,因?yàn)橹骶€程被死循環(huán)阻塞,無法響應(yīng)任何操作。

2.2 異步機(jī)制的引入

為了防止主線程長時(shí)間阻塞,JavaScript 借助瀏覽器的其他線程(如計(jì)時(shí)器線程、網(wǎng)絡(luò)線程等)來異步處理任務(wù):

1.主線程將異步任務(wù)交由其他線程執(zhí)行(如 setTimeout 的計(jì)時(shí)由 Web APIs 中的 Timer 線程負(fù)責(zé))。

2.等任務(wù)完成后,將“回調(diào)函數(shù)”包裝成任務(wù)加入任務(wù)隊(duì)列。

3.主線程空閑后,從任務(wù)隊(duì)列中取出這些任務(wù)執(zhí)行。

這樣就實(shí)現(xiàn)了非阻塞的異步模型。

2.3 異步機(jī)制的解讀

JavaScript 是一門單線程語言,因?yàn)樗\(yùn)行在瀏覽器的渲染主線程上,而該線程是唯一的。

渲染主線程需要處理多項(xiàng)任務(wù),包括頁面渲染和執(zhí)行 JavaScript 代碼等。如果采用同步執(zhí)行方式,可能會導(dǎo)致主線程阻塞,進(jìn)而使消息隊(duì)列中的其他任務(wù)無法及時(shí)執(zhí)行。這不僅會造成主線程資源的浪費(fèi),還會導(dǎo)致頁面更新延遲,給用戶帶來卡頓的體驗(yàn)。

為此,瀏覽器采用異步機(jī)制來解決這個(gè)問題。當(dāng)遇到計(jì)時(shí)器、網(wǎng)絡(luò)請求、事件監(jiān)聽等任務(wù)時(shí),主線程會將這些任務(wù)交由其他線程處理,自身則繼續(xù)執(zhí)行后續(xù)代碼。待其他線程完成任務(wù)后,會將回調(diào)函數(shù)封裝成新任務(wù),添加到消息隊(duì)列末尾等待主線程調(diào)度執(zhí)行。

這種異步機(jī)制有效避免了瀏覽器阻塞,確保了單線程運(yùn)行的流暢性。

三、事件循環(huán)機(jī)制

3.1 事件循環(huán)(event loop)

事件循環(huán)是瀏覽器或 Node.js 中控制異步執(zhí)行的核心機(jī)制。它的本質(zhì)就是一個(gè)不斷循環(huán)的系統(tǒng):

while (true) {
  // 1. 執(zhí)行一個(gè)宏任務(wù)(task)
  // 2. 執(zhí)行完立即清空所有微任務(wù)(microtasks)
  // 3. 如果需要更新界面,則進(jìn)行渲染
}

每一次完整的循環(huán)稱為一個(gè)“tick”。在每個(gè) tick 中,主線程都會從任務(wù)隊(duì)列中取出一個(gè)宏任務(wù)執(zhí)行,并在其執(zhí)行完畢后立即清空微任務(wù)隊(duì)列。

3.2 任務(wù)隊(duì)列

現(xiàn)代瀏覽器不再僅使用“宏任務(wù)隊(duì)列 + 微任務(wù)隊(duì)列”的模型,而是根據(jù)任務(wù)來源將宏任務(wù)進(jìn)一步拆分成多個(gè)隊(duì)列,例如:

延時(shí)隊(duì)列:setTimeout, setInterval

用戶交互隊(duì)列:如 click、input 等事件

UI 渲染隊(duì)列:requestAnimationFrame

網(wǎng)絡(luò)事件隊(duì)列:fetch、xhr

這些消息隊(duì)列中的任務(wù)采用先進(jìn)先出機(jī)制,沒有獨(dú)立優(yōu)先級,但消息隊(duì)列本身具有優(yōu)先級劃分。

每個(gè)任務(wù)都有特定類型,同類型任務(wù)必須歸入同一隊(duì)列,不同類型任務(wù)可分配到不同隊(duì)列。在單次事件循環(huán)中,瀏覽器可根據(jù)實(shí)際情況從不同隊(duì)列中選取任務(wù)執(zhí)行。

瀏覽器必須維護(hù)專門的微隊(duì)列,該隊(duì)列中的任務(wù)享有最高執(zhí)行優(yōu)先級。

當(dāng)前主流隊(duì)列按優(yōu)先級排序?yàn)椋貉訒r(shí)隊(duì)列(中優(yōu)先級)、交互隊(duì)列(高優(yōu)先級)、微隊(duì)列(最高優(yōu)先級)。

3.3 瀏覽器中一輪事件循環(huán)的過程

根據(jù)上述對事件循環(huán)和異步任務(wù)的解釋,可以得出瀏覽器中的一輪事件的循環(huán)過程

  1. 取出一個(gè)宏任務(wù)執(zhí)行

  2. 執(zhí)行過程中可能注冊多個(gè)微任務(wù)(如 Promise.then)

  3. 執(zhí)行完宏任務(wù)后,立即依次執(zhí)行所有微任務(wù)

  4. 執(zhí)行 UI 更新與渲染

  5. 進(jìn)入下一輪循環(huán),重復(fù)上述流程

四、相關(guān)問題的解答

1. JS為什么會阻塞渲染

由于 JS 腳本的執(zhí)行與頁面渲染共用主線程,若 JS 長時(shí)間執(zhí)行,渲染任務(wù)就會延后。

例如以下同步阻塞代碼:

while(Date.now() < performance.now() + 2000) {}

頁面會在 2 秒內(nèi)完全無法響應(yīng)。

此外,瀏覽器為了防止 JS 操作 DOM 導(dǎo)致的視覺中斷,在 JS 執(zhí)行期間通常會“暫停渲染”,等腳本執(zhí)行完畢再統(tǒng)一渲染頁面。

2. JS的計(jì)時(shí)器能做到真正的精確計(jì)時(shí)嘛

(1)計(jì)算機(jī)硬件和操作系統(tǒng)的限制

        CPU時(shí)鐘精度問題,計(jì)算機(jī)沒有原子鐘,無法精確計(jì)算時(shí)間

        操作系統(tǒng)提供的計(jì)時(shí) API(如 Windows 的 GetSystemTime)存在 1-15ms 的調(diào)用誤差。

(2)瀏覽器的引擎機(jī)制

        嵌套超限補(bǔ)償,當(dāng)定時(shí)器嵌套層級超過 5 層時(shí)(如回調(diào)中再次調(diào)用 setTimeout),瀏覽器會強(qiáng)制增加 4ms 延遲:

// 假設(shè)這是第六次調(diào)用,調(diào)用開始產(chǎn)生額外延遲,即使設(shè)置延遲為0,也會有至少4ms延遲
setTimeout(function recur() {
  setTimeout(recur, 0); // 實(shí)際延遲 ≥4ms
}, 0);

        最小時(shí)間間隔,即使設(shè)置 setTimeout(fn, 0),實(shí)際延遲通常被限制為 1ms(Chrome)或 4ms(舊版瀏覽器)。

(3)事件循環(huán)調(diào)度延遲

        主線程阻塞,當(dāng)同步代碼或長任務(wù)占用主線程時(shí),計(jì)時(shí)器回調(diào)必須等待。   

        隊(duì)列優(yōu)先級競爭,即使準(zhǔn)時(shí)到期,回調(diào)仍需等待:微任務(wù)隊(duì)列清空、更高優(yōu)先級的交互/動畫任務(wù)、當(dāng)前執(zhí)行棧為空。

總結(jié)

到此這篇關(guān)于JS的運(yùn)行機(jī)制之事件循環(huán)機(jī)制的文章就介紹到這了,更多相關(guān)JS事件循環(huán)機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Bootstrap彈出框(Popover)被擠壓的問題小結(jié)

    Bootstrap彈出框(Popover)被擠壓的問題小結(jié)

    比較了下Bootstrap的popover和一些其它的開源項(xiàng)目,覺得Bootstrap的還算不錯(cuò)。在使用過程中遇到了一系列問題,下面小編給大家分享Bootstrap彈出框(Popover)被擠壓的問題小結(jié),需要的朋友參考下吧
    2017-07-07
  • JS和css實(shí)現(xiàn)檢測移動設(shè)備方向的變化并判斷橫豎屏幕

    JS和css實(shí)現(xiàn)檢測移動設(shè)備方向的變化并判斷橫豎屏幕

    這篇文章主要介紹了JS和css實(shí)現(xiàn)檢測移動設(shè)備方向的變化并判斷橫豎屏幕,本文分別給出實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2015-05-05
  • JSONObject使用方法詳解

    JSONObject使用方法詳解

    JSONObject-lib包是一個(gè)beans,collections,maps,java arrays和xml和JSON互相轉(zhuǎn)換的包,本文給大家介紹jsonobject使用方法相關(guān)知識,感興趣的朋友一起學(xué)習(xí)吧
    2015-12-12
  • Javascript單線程和事件循環(huán)

    Javascript單線程和事件循環(huán)

    這篇文章主要介紹了Javascript單線程和事件循環(huán),文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-06-06
  • JS實(shí)現(xiàn)基數(shù)排序的示例代碼

    JS實(shí)現(xiàn)基數(shù)排序的示例代碼

    基數(shù)排序是一種根據(jù)數(shù)字位數(shù)的值,對整數(shù)進(jìn)行排序的算法,本文主要介紹了JS實(shí)現(xiàn)基數(shù)排序的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12
  • JavaScript運(yùn)動框架 鏈?zhǔn)竭\(yùn)動到完美運(yùn)動(五)

    JavaScript運(yùn)動框架 鏈?zhǔn)竭\(yùn)動到完美運(yùn)動(五)

    這篇文章主要為大家詳細(xì)介紹了JavaScript運(yùn)動框架的第五部分,鏈?zhǔn)竭\(yùn)動到完美運(yùn)動,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • 詳解JavaScript樹結(jié)構(gòu)

    詳解JavaScript樹結(jié)構(gòu)

    對于數(shù)據(jù)結(jié)構(gòu)“樹”,想必大家都熟悉,本文,我們就再來回顧一下數(shù)據(jù)結(jié)構(gòu)中的二叉樹與樹,并用JavaScript實(shí)現(xiàn)它們。下面就跟著小編一起來看下吧
    2017-01-01
  • Javascript+CSS實(shí)現(xiàn)影像卷簾效果思路及代碼

    Javascript+CSS實(shí)現(xiàn)影像卷簾效果思路及代碼

    Arcmap里面的一個(gè)卷簾效果肯定記憶很深刻,我也對這種比較炫的卷簾效果做了一下研究,現(xiàn)在給大家匯報(bào)下結(jié)果
    2014-10-10
  • 詳解JavaScript中.splice()的用法

    詳解JavaScript中.splice()的用法

    Array.prototype.splice()是JavaScript中用于在數(shù)組的指定位置添加或刪除元素的方法,同時(shí)直接修改原數(shù)組,本文就來詳細(xì)的介紹一下.splice()的用法小結(jié),感興趣的可以了解一下
    2025-10-10
  • Knockoutjs 學(xué)習(xí)系列(二)花式捆綁

    Knockoutjs 學(xué)習(xí)系列(二)花式捆綁

    這篇文章主要介紹了Knockoutjs 學(xué)習(xí)系列(二)花式捆綁 的相關(guān)資料,主要介紹了knockoutjs中各種綁定的使用方法,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-06-06

最新評論

海晏县| 莱西市| 天全县| 泸州市| 莆田市| 萨迦县| 北京市| 台东县| 霞浦县| 长岭县| 库尔勒市| 杨浦区| 崇左市| 贵定县| 四川省| 盈江县| 什邡市| 长岭县| 汉寿县| 宽城| 湘乡市| 民和| 宾阳县| 清水河县| 大城县| 新宁县| 斗六市| 黄平县| 本溪市| 兴海县| 英德市| 兴海县| 遂宁市| 同德县| 米易县| 凭祥市| 河间市| 营口市| 安塞县| 荔波县| 台山市|