JavaScript的事件循環(Event loop)(附圖)

這是本人的理解,而且Google browser斷點實踐的,不一樣瀏覽器可能存在差別。章末附帶外文視頻地址。

JS特色

javascript一門單線程的非阻塞的腳本語言。當用戶進行點擊元素,又進行元素移除,這樣就會致使哪一個事件的優先級不知道,因此JS必須是單線程的。爲了協調事件,用戶交互,腳本,UI渲染,和網絡處理,Event Loop能夠防止主線程阻塞。在某些花費時間較長的事件,瀏覽器會將它們掛起(pending),事件等待被執行,事件循環是經過任務隊列實現的。javascript

one thread == one callback == one thing at a time.java

Event Loop
  • Browing Context
  • Worker 二者是獨立的,有各自的線程環境。
棧(Stack)

棧是一種遵循後進先出(LIFO)的數據集合,新添加或待刪除的元素都保存在棧的末尾,稱做棧頂,另外一端稱做棧底。在棧裏,新元素都靠近棧頂,舊元素都靠近棧底web

隊列(Queue)

隊列是一種遵循先進先出(FIFO)的數據集合,新的條目會被加到隊列的末尾,舊的條目會從隊列的頭部被移出。ajax

微任務與任務
  • 任務task:script(總體代碼)、setTimeout、setInterval、I/O(輸入輸出設備)、UI交互事件,setImmediate(Node.js環境),webAPI callback()等
  • 微任務:Promise、MutationObserver、process.nextTick(Node.js環境)

JS執行規則

能夠這樣理解,有兩個隊列—— 執行隊列(同步任務synchronous)、事件隊列(異步任務asynchronous),存放全部着JS執行的任務(ajax、點擊事件、UI渲染等、promise.then)。promise

  • 執行隊列存放全部同步代碼的任務
  • 事件隊列存放全部異步代碼的宏任務
  • 微任務處於兩個隊列之間

運行Js代碼,從上往下執行代碼,同步代碼,依次執行,遇到異步代碼,則根據其任務類型,添加到相應的對應隊列,宏任務放入事件隊列,微任務放於執行隊列以後(可是會是某個事件循環的最後),事件隊列(任務)以前。這個處理事件過程是不斷循環的,只要主線程空了,就會去讀取"任務隊列"而且執行。瀏覽器

demo
<script>
    console.log('script start');

    setTimeout(function() {
        console.log('timeout1');
    }, 0);

    new Promise(resolve => {
        console.log('promise1');
        resolve();
        setTimeout(() => {
            console.log('timeout2');
            new Promise(resolve => {
                resolve();
            }).then(function() {
                console.log('then3')
            })
        }, 0);
    }).then(function() {
        console.log('then1')
    })
    new Promise(resolve => {
        resolve();
    }).then(function() {
        console.log('then2')
    })

    console.log('script end');
</script>

複製代碼

image

總結任務

  • 任務(tasks)按循序執行,瀏覽器或許在一邊執行任務(tasks)一邊更新渲染
  • 微任務(microtasks)也是按順序執行
    • 在每一次回調以後執行,只要沒有其餘js正在執行
    • 在每一次任務以後
  • 當前執行棧執行完畢時會馬上先處理全部微任務隊列中的事件,而後再去宏任務隊列中取出一個事件。
參考
相關文章
相關標籤/搜索