淺談js的事件循環(Event Loop)

  • 事件循環是js這門語言的一大特色。
  • 瞭解事件循環機制,有助於平常開發中遇到的一些異步問題。
  • 並且仍是前端面試一常常考點。
  • 故本人結合一些文章和我的的一些開發經驗,淺淡一下

一,js是一門單線程語言

  1. js的單線程
a. js是一門單線程的語言。這意味着它在同一時間,只能作同一件事。
b. 但爲了協調事件,用戶交互,UI渲染和網絡行爲交互等。
c. 防止主線程被阻塞,Event Loop便應運而生。
如: 發送一個網絡請求,須要等待必定時間,這個時間內主線程空閒出來作些其餘事;
複製代碼
  1. 爲何js是單線程?
a. js主要是運行在瀏覽器的腳步語言,主要是操做dom;
b. 舉個例子,若是js同時有多個線程。多個線程同時操做同一個dom,
   這時瀏覽器該依據那個線程,如何判斷優先級
c. 爲了不上述問題,並下降複雜度,故js被設計成單線程語言。
複製代碼

二,概念的理解

  1. 同步任務
同步任務指的是,在主線程上排隊執行的任務,
只有前一個任務執行完畢,才能執行後一個任務;
複製代碼
  1. 異步任務
異步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,
只有"任務隊列"通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行。
複製代碼
  1. 異步執行機制
a. 全部同步任務都在主線程上執行,造成一個執行棧(execution context stack);
b. 主線程以外,還存在一個"任務隊列"(task queue)。
   只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。
c. 一旦"執行棧"中的全部同步任務執行完畢,系統就會讀取"任務隊列",
   看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,
   進入執行棧,開始執行。
d. 主線程不斷重複上面的第三步。
複製代碼
  1. 任務隊列
"任務隊列"是一個先進先出的數據結構,排在前面的事件,優先被主線程讀取。
複製代碼
  1. 事件循環
主線程從"任務隊列"中讀取事件,這個過程是循環不斷的,
因此整個的這種運行機制又稱爲Event Loop(事件循環)。
複製代碼
  1. 宏任務與微任務
異步任務分爲 宏任務(macrotask) 與 微任務 (microtask),
不一樣的API註冊的任務會依次進入自身對應的隊列中,
而後等待 Event Loop 將它們依次壓入執行棧中執行。

宏任務:script(總體代碼)、setTimeout、setInterval、UI 渲染、 
        I/O、postMessage、 MessageChannel、setImmediate(Node.js 環境)
        
微任務:Promise、 MutaionObserver、process.nextTick(Node.js環境)
複製代碼
  1. Event Loop(事件循環)
1)執行棧選擇最早進入隊列的宏任務(一般是script總體代碼),若是有則執行;
(2)檢查是否存在 Microtask,若是存在則不停的執行,直至清空 microtask 隊列;
(3)更新render(每一次事件循環,瀏覽器均可能會去更新渲染);
(4)重複以上步驟;
複製代碼
  1. 宏任務 > 全部微任務(核心),上代碼
<script>// 宏任務1
    console.log('宏任務1'); // 宏任務1中的同步任務
    
    setTimeout(() => {// 宏任務1中的另外一個宏任務3
        console.log('宏任務1中的另外一個宏任務3');
        
        new Promise((resolve, reject) => {
            resolve('宏任務3中的微任務2');
        }).then(data => {// 宏任務3中的微任務2
            console.log(data)
        })
        
    }, 300);
    
    new Promise((resolve, reject) => {
        resolve('宏任務1中的微任務1');
    }).then(data => {// 宏任務1中的微任務1
        console.log(data);
        
        setTimeout(() => {// 微任務1中的另外一個宏任務4
            console.log('微任務1中的另外一個宏任務4');
        }, 300);
        
    });
    
</script>// 宏任務1

<script>// 宏任務2
    console.log('宏任務2')
</script>// 宏任務2
複製代碼
  • 上述代碼的執行結果

  • 代碼結果分析:
1. 宏任務1=>宏任務1中的微任務1
   代表執行完宏任務就執行微任務(忽略宏任務2,便於理解)
2. 而後到 宏任務1中宏任務3=>宏任務3中的微任務2
   再次代表執行完本宏任務後就執行本宏任務下的微任務
3. 最後到微任務1中的宏任務4
複製代碼
相關文章
相關標籤/搜索