性能優化篇 - js事件循環機制(event loop)

前言

以前在作前端的時候,可能注重的東西更偏向於業務層面的東西,切圖、實現交互、調用接口等一系列比較淺的技術層,隨着前端技術的不斷髮展,只掌握這些知識是不夠的,要學會去了解如何從一個網址,渲染出來一個頁面,在到後來可讓你看到你想看到的東西,並去操做它,瞭解瀏覽器底層的一些渲染機制,是做爲一個優秀的前端必不可少的,這篇文章講解的就是有關js單線程是如何執行的一篇文章,若有不足之處,請指出,我會及時做出改正。javascript


event loop在咱們的平常工做當中,涉及到的地方有不少,只是你們可能不知道這個東西,就是event loop機制致使的這種渲染方式,舉個🌰:
console.log(1)
setTimeout(()=>{
    console.log(3)
},1)
console.log(2)
複製代碼

顯而易見,這個結果就是:一、二、3html

無論setTimeout放在當前做用域的什麼位置,結果都是一、二、3,爲何,這就是event loop事件循環機制。前端

你們都知道,js是單線程的,若是對多進程和多線程不是特別瞭解,能夠參考這兩篇文章:java

瀏覽器多進程架構promise

瀏覽器渲染進程多線程瀏覽器

經過這兩篇文章你瞭解什麼是多進程多線程之後,可能看起來這裏會好不少。多線程

上面的例子,其實還涉及到了一個問題,就是我setTimeout設置的時間是1ms,這裏要注意:架構

w3c在HTML標準中規定,要求setTimeout時間低於4ms的都按4ms來算異步

這裏還要注意一點,就是有些時候,爲何一些大神用js作一些相似於動畫操做的時候,喜歡用setTimeout,而不是用setInterval呢,由於setTimeout是在這個時間後,把當前的方法推到任務隊列裏,而setInterval是強行把當前的方法添加到任務隊列裏,這樣可能會對當前頁面的用戶體驗很很差,可能會出現效果卡頓的狀況,因此這裏還要注意一點:函數

setTimeout是延遲執行,可是不是延遲的時間後當即執行的

其實,event loop它最主要是分三部分:主線程、宏隊列(macrotask)、微隊列(microtask)

js的任務隊列分爲同步任務和異步任務,全部的同步任務都是在主線程裏執行的,異步任務可能會在macrotask或者microtask裏面

主線程

主線程就是簡單點的理解,就是訪問到的script標籤裏面包含的內容,或者是直接訪問某一個js文件的時候,裏面的能夠在當前做用域直接執行的全部內容(執行的方法,new出來的對象等),都是在主線程裏面作的,添加到任務隊列裏面的就不算了。仍是老樣子,舉個🌰:

//index.html
<html>
    <head></head>
    <body>
        <script src="index.js"></script>
        <script>
            console.log(1)
            setTimeout(() => {
               console.log(3) 
            },1)
        </script>
    </body>
</html>

//index.js
console.log(2)
setTimeout(() => {
   console.log(4) 
},1)
複製代碼

結果1和2都是在主線程裏面執行的,3和4被扔到了任務隊列裏面。

宏隊列(macrotask)

setTimeout、setInterval、setImmediate、I/O、UI rendering

微隊列(microtask)

promise.then、process.nextTick、Object.observe(已廢棄)


先上代碼
console.log(1)
process.nextTick(() => {
  console.log(8)
  setTimeout(() => {
    console.log(9)
  })
})
setTimeout(() => {
  console.log(2)
  new Promise(() => {
    console.log(11)
  })
})
requestIdleCallback(() => {
  console.log(7)
})
let promise = new Promise((resolve,reject) => {
  setTimeout(() => {
    console.log(10)
  })
  resolve()
  console.log(4)
})
fn()
console.log(3)
promise.then(() => {
  console.log(12)
})
function fn(){
  console.log(6)
}
複製代碼

結果是一、四、六、三、十二、八、二、十一、十、九、7

這個寫法能夠囊括80%以上的event loop循環機制的場景了,下面開始梳理具體的運行機制。

js是從上到下執行的,因此上來先打印的是 1 ,繼續往下走;

碰見了process.nextTick,由於它屬於微隊列(microtask),而且當前主線程的代碼尚未執行完畢,因此它被展現扔到了微隊列裏,暫時不打印;

這個時候又遇到了setTimeout,setTimeout是屬於宏隊列(macrotask);

requestIdleCallback,這裏也是不當即執行的,它也不屬於任何隊列,這裏不作詳細解釋;

promise在實例化的時候,這裏的setTimeout繼續被丟到了宏隊列(macrotask)中,並執行了成功的方法,在有promise.then的調用的時候就會去出發,但這裏不作打印,接着發現了console,這裏直接打印 4

fn函數直接調用,直接打印 6

console,直接打印 3

promise.then由於它屬於微隊列,可是它在promise實例化的時候被調用了,因此它會在微隊列的最前面執行;

到這裏主線程裏面就沒有任何能夠執行到東西了,下面開始走微隊列(microtask):

因爲promise.then被提早調用了,因此它會先執行,打印 12

微隊列(microtask)裏面還有一個,就是上面的process.nextTick,執行它,打印 8 ,這個時候發現它有一個setTimeout,放到宏隊列(macrotask);

到這裏微隊列就走完了,下面開始走宏隊列(macrotask):

最外面的setTimeout在一開始的時候被放了進去,因此先執行它,打印 2 ,發現它裏面有promise被實例化,直接執行,打印 11

下一個要走的就是promise裏面的setTimeout,打印 10

還剩最後一個setTimeout,就是process.nextTick裏面的,打印 9

到這裏主線程、宏隊列(macrotask)、微隊列(microtask)就全都跑完了,在所有跑完的時候,requestIdleCallback纔會執行,打印 7

requesIdleCallback會在當前瀏覽器空閒時期去依次執行,在整個過程中你可能添加了多個requestIdleCallback,可是都不會執行,只會在空閒時期,去依次根據調用的順序就執行。

console.log(1)
process.nextTick(() => {
  console.log(8)
  setTimeout(() => {
    console.log(9)
    requestIdleCallback(() => {
      console.log(13)
    })
  })
})
setTimeout(() => {
  console.log(2)
  new Promise(() => {
    console.log(11)
  })
})
requestIdleCallback(() => {
  console.log(7)
})
let promise = new Promise((resolve,reject) => {
  setTimeout(() => {
    console.log(10)
  })
  resolve()
  console.log(4)
  requestIdleCallback(() => {
    console.log(14)
  })
})
fn()
console.log(3)
promise.then(() => {
  console.log(12)
  requestIdleCallback(() => {
    console.log(15)
  })
})
function fn(){
  console.log(6)
  requestIdleCallback(() => {
    console.log(16)
  })
}
複製代碼

結果是一、四、六、三、十二、八、二、十一、十、九、七、1四、1六、1五、13

由於這一塊不涉及到事件循環裏面,可是屬於一個比較例外的方法,這裏只作簡單講解,不作深刻研究。

總結

其實,event loop用簡單點的話去解釋,就是:

一、先執行主線程

二、遇到宏隊列(macrotask)放到宏隊列(macrotask)

三、遇到微隊列(microtask)放到微隊列(microtask)

四、主線程執行完畢

五、執行微隊列(microtask),微隊列(microtask)執行完畢

六、執行一次宏隊列(macrotask)中的一個任務,執行完畢

七、執行微隊列(microtask),執行完畢

八、依次循環。。。

這個過程,其實就是咱們具體要說的js事件循環機制(event loop)。


結束語

這些,是我在網上看一些大神的講解,和本身對js事件循環機制(event loop)的理解,寫的一篇總結性的文章,若是當中有哪些寫的不對的地方,還請你們指出,我會在最短期內做出調整。若是以爲謝的還行,幫忙給個贊吧。

相關文章
相關標籤/搜索