JavaScript — 事件循環 eventloop

前提梳理

  1. eventloop機制是C++寫的,不屬於JS。JS是個單線程的語言,來了任務就執行。
  2. eventloop機制在nodejs和chrome瀏覽器中是不一樣的。下面咱們先梳理nodejs,再講chrome

Node.js的eventloop

概念圖

timers  --->   poll   --->  check
  ↑      在poll階段停留等待    |
  𠃊_________________________𠃎

當執行一個異步任務發生了什麼(理想狀況)

  • 普通異步任務node

    setTimeout(fn,1000)
    1. JS把setTimeout放在timers階段的一個數組裏,並記錄下1000ms
`[{event:fn,time:1000ms}]`
2. JS就無論setTimeout這個任務了,繼續去執行JS同步代碼
3. 在poll階段等待1000ms,時間到後到timers階段執行fn
  • 普通異步任務+setImmdiatechrome

    setTimeout(fn1,1000)
    setImmidiate(fn2)
    1. JS把setTimeout放在timers階段的一個數組裏,並記錄下時長1000ms
    2. JS就無論setTimeout這個任務了,繼續去執行JS同步代碼
    3. 在poll階段等待,這時候發現setImmideate任務來了,再也不等待,跳到check階段
    4. check階段有個數組,裏邊存放了fn2. 帶着fn2跳轉到timers執行
    5. 執行後發現1000ms仍是沒到,因而回到poll階段等待,到了1000ms後再通過check階段跳轉到timers,執行fn

奇怪現象

相信你注意到了上個板塊中的「理想狀況」四個字。
什麼叫理想狀況?那就是咱們假定eventloop的啓動要比js的啓動更快。數組

//test.js
setTimeout(()=>{ console.log('fn1') },1000)
setImmidiate(()=>{ console.log('fn2') })

$ node test.js 
//輸出:fn1 fn2
$ node test.js
//輸出:fn2 fn1

你會發現,一樣的代碼竟然產生了不同的結論

因此說,eventloop和js代碼,誰啓動的更快就決定了執行的順序!瀏覽器

  • 假設eventloop更快:那麼timers階段,數組裏並無存入setTimout,直接進入poll階段等待,這時候js才執行。那eventloop先執行fn2,在下一輪執行fn1
  • 假設js執行更快:那麼timers階段,數組裏就已經存在了setTimeout,那麼久先執行fn2,再執行fn2

複雜題目

const fn = ()=>{
    setImmidiate(()=>{
      console.log('a')
      setTimeout(()=>{
         console.log('b')
     },0)
    })
    setTimeout(()={
     console.log('c')
     setImmidiate(()=>{
            console.log('d')
        })
    },0)
}
fn()

輸出:a c b d異步

Chrome的eventloop

宏任務和微任務

  1. 宏任務:一下子就作的異步任務async

    • setTimeout 等
  2. 微任務:立刻就作的異步任務ide

    • Promise.then 等
  3. 實例oop

    setTimeout(() => console.log(4)) 
    new Promise(resolve => {  
       resolve() 
       console.log(1) 
    }).then(() => { 
       console.log(3) 
    }) 
    console.log(2)

    輸出: 1 2 3(微任務) 4(宏任務)idea

進階題目

async function fn1(){
    console.log(1)
    await fn2()
    console.log(2)
}
async function fn2(){
    console.log(3)
}
fn1()

new Promise(function(resolve){
    console.log(4)
    resolve()
}).then(()=>{
    console.log(5)}
)

輸出:1 3 4 2 5線程

tips:

  • await 展開

    await fn2(); console.log(2)
    //能夠合併爲 
    fn2().then(()=>{ console.log(2) })
相關文章
相關標籤/搜索