事件迴圈深度學習

語言: CN / TW / HK

事件迴圈

學習事件迴圈 首先就要了解一下瀏覽器的程序模型

瀏覽器的程序模型

何為程序?

  • 如果要執行一個程式 就要屬於他自己的記憶體空間 可以把這塊記憶體空間簡單理解為程序
  • 什麼物件,函式啥啥啥的 都是放在記憶體裡面的 所以就需要開闢一塊記憶體空間讓這個程式自己去使用
  • 總結 : 程序 == 開闢出的這塊記憶體空間

    每一個應用 至少也要有一個程序 程序之間相互獨立 即使要通訊 也需要雙方同意才行 比如qq程序崩潰了 肯定是不會影響到微信的 所以每一個程序是完全獨立的

何為執行緒?

  • 有了程序後 就可以執行程式的程式碼了
  • 執行程式碼的稱之為執行緒

    一個程序至少有一個執行緒 所以在程序開啟後會自動建立一個執行緒來執行程式碼 該執行緒稱之為主執行緒

    如果程式需要同時執行多塊程式碼 主執行緒就會啟動更多的 執行緒來執行程式碼 所以一個程序中可以包含多個執行緒

瀏覽器有哪些程序和執行緒

瀏覽器是一個多程序多執行緒的應用程式

  • 瀏覽器內部工作極其複雜
  • 為了避免相互影響 為了減少連環崩潰的機率 當瀏覽器啟動後 它會開啟多個程序

可以在瀏覽器的工作管理員中檢視當前的所有程序

其中最主要的程序有
  • 瀏覽器程序 : 主要負責頁面的顯示 使用者互動 子程序管理等 瀏覽器程序內部會啟動多個執行緒處理不同的任務
  • 網路程序 : 負責載入網路資源 網路程序內部會啟動多個執行緒來處理不同的網路任務
  • 渲染程序(重點) : 渲染程序啟動後 會開啟一個渲染主執行緒 , 主執行緒負責執行HTML,CSS,JS程式碼

    預設情況下瀏覽器會為為每個標籤頁開啟一個新的渲染程序 以保證不同的標籤頁之間不相互影響

渲染主執行緒是如何工作的?

ps: 事件迴圈就在主執行緒中執行 - 渲染主執行緒是瀏覽器中最繁忙的執行緒 需要他處理的任務非常多 - 解析html,css,js,計算樣式,佈局,處理圖層,每秒把頁面畫60次.....

要處理這麼多的任務 主執行緒如何去排程指揮任務呢?

比如: - 我正在執行一個js函式,執行到一半的時候使用者點選了按鈕, 我該立即去執行點選事件的處理函式嗎? - 我正在執行一個js函式,執行到一半額時候某個計時器到達了時間,我改立即去實現它的回撥函式嗎? - 瀏覽器程序通知我使用者點選了按鈕 ,與此同時,某個計時器也到達了時間,我應該處理哪一個呢? ### 渲染主執行緒指揮排程核心的方法 排隊

以下的整個過程,被稱之為事件迴圈 也可以說訊息迴圈 - 在最開始的時候 渲染主執行緒會進入一個無限的迴圈 - 每一次迴圈會檢查事件佇列中是否有任務存在,如果有,就取出第一個任務執行,執行完後進入下一次迴圈,如果沒有就休息 - 其他所有執行緒 (包括其他進行的執行緒比如網路執行緒,監聽使用者互動的執行緒等等..) ,可以隨時向事件佇列裡新增任務,新任務會加到事件佇列的末尾,在新增新任務的時候,如果主執行緒是休眠狀態,則會喚醒以繼續迴圈拿去任務

事件迴圈的細節

非同步是什麼?

在程式碼執行過程中, 會遇到一些無法立即處理的任務 - 計時完成後需要執行的任務---setTimeOut,setInterval - 網路通訊後需要執行的任務---XHR,Fetch - 使用者操作後需要執行的任務---addEventListener

執行這些任務 都是需要未知的時間的 ,不能立即去執行

如果讓渲染主執行緒等待這型別任務的時機達到,就會導致主執行緒長期處於阻塞的狀態 從而導致瀏覽器卡死!!! ```javascript setTimeOut(()=>{

console.log(1);

},300)

console.log(2); // 如果先等待3秒後列印1 然後再列印2 這就是同步 // 顯然 這樣是不可行的 因為一個定時器所有的任務都被阻塞了 // 那渲染主執行緒怎麼執行繪製頁面任務?怎麼執行事件互動任務? 直接廢了! ```

渲染主執行緒承擔著極其重要的工作,無論如何都不能阻塞!!!!

因此瀏覽器選擇用非同步來解決這個問題

  • 比如主執行緒在事件佇列中遇到一個定時器
  • 主執行緒會派一個計時執行緒去開始執行定時 主執行緒是不會自己計時的, 他要幹別的活,此時,在主執行緒眼裡,定時器這個的任務已經執行了
  • 主執行緒然後繼續去執行後面的任務
  • 突然定時器時間到了!, 定時器會把需要執行的回撥函式繼續放在事件佇列裡的末端 當成一個普通的任務去執行
  • 此時,主執行緒也不會知道 這個任務就是當時定時器裡的回撥函式 依舊進行迴圈的執行任務

這樣使用非同步的方式: 渲染主執行緒永不阻塞!!!!

之前遇到過一個面試題 , 如何理解js的非同步?

學到這裡後 , 我現在會選擇這樣去回答 - js是一個單執行緒的語言 , 這是因為js只能執行在瀏覽器的渲染主執行緒中,而渲染主執行緒只有一個 , 而渲染主執行緒承擔著非常的多的事情 ,渲染頁面 , 執行js啥啥的 都在裡面執行 - 如果使用同步的方式 , 就極可能導致主執行緒阻塞 , 從而導致訊息佇列中的很多其他任務無法得到執行 , 這樣一來 , 繁忙的主執行緒白白浪費時間等待 , 導致頁面也會無法更新 , 給使用者造成卡死的狀態 - 所以瀏覽器選擇用非同步方式來避免 , 具體做法就是當某些任務發生時 , 比如計時器 , 網路 , 事件監聽 ,主執行緒將任務會交給其他執行緒去處理 , 自身立刻結束任務的執行 , 轉而執行後面的任務 , 當其他執行緒完成時 , 把那個回撥函式包裝成任務 , 加入到事件佇列的末端排隊 , 等待主執行緒排程 - 這樣的一個模式 就是非同步 可以保證瀏覽器不阻塞

js為何阻塞渲染?

  • 因為js和渲染都處於一個執行緒上面
  • 假如點選按鈕修改文字 點選完按鈕後 文字已經修改了 但是還沒繪製在頁面上 要等渲染任務執行後才會繪製

任務有優先順序嗎?

任務沒有優先順序

但是訊息佇列 有優先順序

根據w3c的最新解釋

  • 每個任務都有一個任務型別 , 同一個型別的任務必須在一個佇列也就是一共有多個佇列 , 不同型別的任務可以分屬不同的佇列,在一個次事件迴圈中,瀏覽器可以根據實際情況從不同的佇列中區出任務執行
  • 瀏覽器必須準備好一個微佇列 , 微佇列中的任務優先所有其他任務執行他裡面的東西 所有都要給我等 連繪製任務 都要等 就是最高優先順序了

隨著瀏覽器的複雜度急劇提升 W3C不再使用巨集佇列的說法

在目前chrome的實現中 至少包含了下面的佇列 - 延時佇列 : 用於存放計時器到達後的回撥任務 , 優先順序中 - 互動列隊 : 用於存放使用者操作後產生的事件處理任務 , 優先順序高 - 微佇列 : 使用者存放需要最快執行的任務 優先順序最高

新增任務到微佇列的主要方式是使用promise,.... 列如 ```javascript // 立刻把一個函式新增到微佇列 最高執行 promise.resolve().then(函式)

setTimeOut(()=>{ // 第三步執行延時佇列中的任務 console.log(1); },0)

promise.resolve().then(()=>{ // 第二步執行微佇列中的任務 console.log(2); })

console.log(3); // 第一步先執行全域性js

// 3 2 1

```

面試題 : 闡述一下js的事件迴圈

  • 事件迴圈又叫訊息迴圈官方叫event loop 瀏覽器實現叫message loop , 是瀏覽器選擇主執行緒的工作方式 , 在chrome中就是開啟了一個不會結束的for迴圈, 每次迴圈從訊息佇列中取出第一個任務執行,而其他執行緒只需要在合適的時候將任務加入到佇列末尾即可
  • 過去吧訊息佇列簡單分為巨集佇列和微佇列,這種說法目前無法滿足複雜的瀏覽器環境,取而代之的是一種更加靈活多變的處理方式
  • 根據w3c官方的解釋, 每個任務有不同的型別,同類型的任務必須在同一個佇列,不同的任務可以屬於不同的佇列.不同的任務佇列有不同的優先順序,在一次事件迴圈中,由瀏覽器自行決定取那個佇列的任務,但瀏覽器必須有一個微佇列,微佇列的任務一定具有最高的優先順序,必須有限排程執行--- title: 事件迴圈深度學習 tags: [js] date: 2022-09-31 16:09:29 categories: [js]

事件迴圈

學習事件迴圈 首先就要了解一下瀏覽器的程序模型

瀏覽器的程序模型

何為程序?

  • 如果要執行一個程式 就要屬於他自己的記憶體空間 可以把這塊記憶體空間簡單理解為程序
  • 什麼物件,函式啥啥啥的 都是放在記憶體裡面的 所以就需要開闢一塊記憶體空間讓這個程式自己去使用
  • 總結 : 程序 == 開闢出的這塊記憶體空間

    每一個應用 至少也要有一個程序 程序之間相互獨立 即使要通訊 也需要雙方同意才行 比如qq程序崩潰了 肯定是不會影響到微信的 所以每一個程序是完全獨立的

何為執行緒?

  • 有了程序後 就可以執行程式的程式碼了
  • 執行程式碼的稱之為執行緒

    一個程序至少有一個執行緒 所以在程序開啟後會自動建立一個執行緒來執行程式碼 該執行緒稱之為主執行緒

    如果程式需要同時執行多塊程式碼 主執行緒就會啟動更多的 執行緒來執行程式碼 所以一個程序中可以包含多個執行緒

瀏覽器有哪些程序和執行緒

瀏覽器是一個多程序多執行緒的應用程式

  • 瀏覽器內部工作極其複雜
  • 為了避免相互影響 為了減少連環崩潰的機率 當瀏覽器啟動後 它會開啟多個程序

可以在瀏覽器的工作管理員中檢視當前的所有程序

其中最主要的程序有
  • 瀏覽器程序 : 主要負責頁面的顯示 使用者互動 子程序管理等 瀏覽器程序內部會啟動多個執行緒處理不同的任務
  • 網路程序 : 負責載入網路資源 網路程序內部會啟動多個執行緒來處理不同的網路任務
  • 渲染程序(重點) : 渲染程序啟動後 會開啟一個渲染主執行緒 , 主執行緒負責執行HTML,CSS,JS程式碼

    預設情況下瀏覽器會為為每個標籤頁開啟一個新的渲染程序 以保證不同的標籤頁之間不相互影響

渲染主執行緒是如何工作的?

ps: 事件迴圈就在主執行緒中執行 - 渲染主執行緒是瀏覽器中最繁忙的執行緒 需要他處理的任務非常多 - 解析html,css,js,計算樣式,佈局,處理圖層,每秒把頁面畫60次.....

要處理這麼多的任務 主執行緒如何去排程指揮任務呢?

比如: - 我正在執行一個js函式,執行到一半的時候使用者點選了按鈕, 我該立即去執行點選事件的處理函式嗎? - 我正在執行一個js函式,執行到一半額時候某個計時器到達了時間,我改立即去實現它的回撥函式嗎? - 瀏覽器程序通知我使用者點選了按鈕 ,與此同時,某個計時器也到達了時間,我應該處理哪一個呢? ### 渲染主執行緒指揮排程核心的方法 排隊

以下的整個過程,被稱之為事件迴圈 也可以說訊息迴圈 - 在最開始的時候 渲染主執行緒會進入一個無限的迴圈 - 每一次迴圈會檢查事件佇列中是否有任務存在,如果有,就取出第一個任務執行,執行完後進入下一次迴圈,如果沒有就休息 - 其他所有執行緒 (包括其他進行的執行緒比如網路執行緒,監聽使用者互動的執行緒等等..) ,可以隨時向事件佇列裡新增任務,新任務會加到事件佇列的末尾,在新增新任務的時候,如果主執行緒是休眠狀態,則會喚醒以繼續迴圈拿去任務

事件迴圈的細節

非同步是什麼?

在程式碼執行過程中, 會遇到一些無法立即處理的任務 - 計時完成後需要執行的任務---setTimeOut,setInterval - 網路通訊後需要執行的任務---XHR,Fetch - 使用者操作後需要執行的任務---addEventListener

執行這些任務 都是需要未知的時間的 ,不能立即去執行

如果讓渲染主執行緒等待這型別任務的時機達到,就會導致主執行緒長期處於阻塞的狀態 從而導致瀏覽器卡死!!! ```javascript setTimeOut(()=>{

console.log(1);

},300)

console.log(2); // 如果先等待3秒後列印1 然後再列印2 這就是同步 // 顯然 這樣是不可行的 因為一個定時器所有的任務都被阻塞了 // 那渲染主執行緒怎麼執行繪製頁面任務?怎麼執行事件互動任務? 直接廢了! ```

渲染主執行緒承擔著極其重要的工作,無論如何都不能阻塞!!!!

因此瀏覽器選擇用非同步來解決這個問題

  • 比如主執行緒在事件佇列中遇到一個定時器
  • 主執行緒會派一個計時執行緒去開始執行定時 主執行緒是不會自己計時的, 他要幹別的活,此時,在主執行緒眼裡,定時器這個的任務已經執行了
  • 主執行緒然後繼續去執行後面的任務
  • 突然定時器時間到了!, 定時器會把需要執行的回撥函式繼續放在事件佇列裡的末端 當成一個普通的任務去執行
  • 此時,主執行緒也不會知道 這個任務就是當時定時器裡的回撥函式 依舊進行迴圈的執行任務

這樣使用非同步的方式: 渲染主執行緒永不阻塞!!!!

之前遇到過一個面試題 , 如何理解js的非同步?

學到這裡後 , 我現在會選擇這樣去回答 - js是一個單執行緒的語言 , 這是因為js只能執行在瀏覽器的渲染主執行緒中,而渲染主執行緒只有一個 , 而渲染主執行緒承擔著非常的多的事情 ,渲染頁面 , 執行js啥啥的 都在裡面執行 - 如果使用同步的方式 , 就極可能導致主執行緒阻塞 , 從而導致訊息佇列中的很多其他任務無法得到執行 , 這樣一來 , 繁忙的主執行緒白白浪費時間等待 , 導致頁面也會無法更新 , 給使用者造成卡死的狀態 - 所以瀏覽器選擇用非同步方式來避免 , 具體做法就是當某些任務發生時 , 比如計時器 , 網路 , 事件監聽 ,主執行緒將任務會交給其他執行緒去處理 , 自身立刻結束任務的執行 , 轉而執行後面的任務 , 當其他執行緒完成時 , 把那個回撥函式包裝成任務 , 加入到事件佇列的末端排隊 , 等待主執行緒排程 - 這樣的一個模式 就是非同步 可以保證瀏覽器不阻塞

js為何阻塞渲染?

  • 因為js和渲染都處於一個執行緒上面
  • 假如點選按鈕修改文字 點選完按鈕後 文字已經修改了 但是還沒繪製在頁面上 要等渲染任務執行後才會繪製

任務有優先順序嗎?

任務沒有優先順序

但是訊息佇列 有優先順序

根據w3c的最新解釋

  • 每個任務都有一個任務型別 , 同一個型別的任務必須在一個佇列也就是一共有多個佇列 , 不同型別的任務可以分屬不同的佇列,在一個次事件迴圈中,瀏覽器可以根據實際情況從不同的佇列中區出任務執行
  • 瀏覽器必須準備好一個微佇列 , 微佇列中的任務優先所有其他任務執行他裡面的東西 所有都要給我等 連繪製任務 都要等 就是最高優先順序了

隨著瀏覽器的複雜度急劇提升 W3C不再使用巨集佇列的說法

在目前chrome的實現中 至少包含了下面的佇列 - 延時佇列 : 用於存放計時器到達後的回撥任務 , 優先順序中 - 互動列隊 : 用於存放使用者操作後產生的事件處理任務 , 優先順序高 - 微佇列 : 使用者存放需要最快執行的任務 優先順序最高

新增任務到微佇列的主要方式是使用promise,.... 列如 ```javascript // 立刻把一個函式新增到微佇列 最高執行 promise.resolve().then(函式)

setTimeOut(()=>{ // 第三步執行延時佇列中的任務 console.log(1); },0)

promise.resolve().then(()=>{ // 第二步執行微佇列中的任務 console.log(2); })

console.log(3); // 第一步先執行全域性js

// 3 2 1

```

面試題 : 闡述一下js的事件迴圈

  • 事件迴圈又叫訊息迴圈官方叫event loop 瀏覽器實現叫message loop , 是瀏覽器選擇主執行緒的工作方式 , 在chrome中就是開啟了一個不會結束的for迴圈, 每次迴圈從訊息佇列中取出第一個任務執行,而其他執行緒只需要在合適的時候將任務加入到佇列末尾即可
  • 過去吧訊息佇列簡單分為巨集佇列和微佇列,這種說法目前無法滿足複雜的瀏覽器環境,取而代之的是一種更加靈活多變的處理方式
  • 根據w3c官方的解釋, 每個任務有不同的型別,同類型的任務必須在同一個佇列,不同的任務可以屬於不同的佇列.不同的任務佇列有不同的優先順序,在一次事件迴圈中,由瀏覽器自行決定取那個佇列的任務,但瀏覽器必須有一個微佇列,微佇列的任務一定具有最高的優先順序,必須有限排程執行
「其他文章」