深刻理解js引擎的執行機制

深刻理解js引擎的執行機制

最近在檢討,不少知識都是隻會用,不理解底層的知識。因此在開發過程當中遇到一些奇怪的比較難解決的bug,在思考的時候就會收到限制。因此,在這裏一點一點補充基礎知識吧。前端

在閱讀以前,請先記住兩點:

  • js是單線程語言
  • js的Event Loop是js的執行機制。深刻理解js的執行,就等與深刻理解js的Event Loop

好啦,下面進入正題面試

1. 靈魂三問:js爲何是單線程的?爲何須要異步?單線程又是怎麼實現異步的呢?

技術的出現,都跟現實世界裏的場景密切相關。一樣的,咱們就結合現實場景,來回答這三個問題。ajax

(1)js爲何是單線程的?

        js是一種腳本語言,腳本語言是爲了縮短傳統的編寫-編譯-連接-運行過程而建立的計算機編程語言,腳本語言不須要編譯,能夠直接用,由解釋器來負責解釋。
        js最初被設計用在瀏覽器中,那麼想象一下,若是瀏覽器中的js是多線程的。。。
場景描述:
        如今有兩個進程:process1和process2,因爲是多線程的js,因此它們對同一個DOM同時進行操做process1刪除了該DOM,而process2編輯了該DOM,同時下達了兩個矛盾的命令,你這讓瀏覽器怎麼執行呢?
        這樣一想,是否是就理解了js爲何被設計成單線程了吧~~~編程


(2)js爲何是異步的?

場景描述:
        若是js不存在異步,只能自上而下執行,若是上一行執行時間很長,好比說沒有網了,那麼下面的代碼就會被阻塞,對於用戶來講,阻塞就意味着「卡死」,這樣致使用戶體驗不好。因爲這個「缺陷」,致使JavaScript的全部網絡操做,瀏覽器事件,都必須是異步執行。
        因此,js是存在異步執行的,好比setTimeout、setInterval、ajax、promisepromise


(3)單線程是怎麼實現異步的?

場景描述:
        經過Event Loop(事件循環),因此說,理解了Event Loop機制,也就理解了js的執行機制啦。瀏覽器

2. js中的Event Loop第一課

舉個栗子:觀察下它的執行機制網絡

console.log(1)
setTimeout(function(){
    console.log(2)
},0);
console.log(3)

毫無疑問:運行結果是1 3 2多線程

也就是說:setTimeout裏的函數並無當即執行,而是延遲了一段時間,知足必定條件後纔去執行的,咱們叫作異步代碼。異步

因此這裏咱們首先知道了js裏的一種分類方式,就是將任務分爲:同步任務和異步任務。編程語言

按照這種分類方式:js的執行機制是

  • 首先判斷js是同步的仍是一步的,同步的就進入主進程,異步的就進入event table(事件表)
  • 異步任務在Event table中註冊函數,當知足觸發條件後,被推入event queue(事件隊列)
  • 同步任務進入主線程後一直執行,直到主線程空閒時,纔會去event queue中查看是否有可執行的異步任務,若是有就推入主進程中

以上三步循環執行,這就是Event Loop
因此上面的栗子,你是否能夠描述它的執行順序了呢?

console.log(1)          //是同步任務,放進主進程裏
setTimeout(function(){  //是異步任務,放進event table,0s以後被推入event queue裏
    console.log(2)
},0);
console.log(3)          //是同步任務,放進主進程裏
//當一、3在瀏覽器控制檯被打印後,主線程去event queue中查看是否有可執行的函數,執行setTimeout裏的函數。

3. js中的Event Loop第二課

因此,上面關於Event Loop就是我對js執行機制的理解,是否是很簡單呢。。。。
慢着!看看下面的這段代碼
又一個栗子:

setTimeout(function(){
    console.log("定時器開始啦~~~");
})
new Promise(function(resolve){
    console.log("立刻執行for循環啦");
    for(var i=0;i<10000;i++){
        i==99&&resolve();
    }
}).then(function(){
    console.log("執行then函數啦")
});
console.log("表演完畢!");

有沒有很熟悉,好像面試過程當中常常遇見這種問題呀~~~
如今,咱們按照上面學到的js執行機制去分析一下下

setTimeout(function(){                 //是異步任務,被放進event table中
    console.log("定時器開始啦~~~");
})
new Promise(function(resolve){         //是同步任務,被放進主進程中,直接執行
    console.log("立刻執行for循環啦");
    for(var i=0;i<10000;i++){
        i==99&&resolve();
    }
}).then(function(){                    //是異步任務,被放進event table中
    console.log("執行then函數啦")
});
console.log("表演完畢!");              //是同步任務,被放進主進程中,直接執行

運行結果是:【立刻執行for循環啦----表演完畢!----執行then函數啦----定時器開始啦~~~】

那麼,難道是異步任務的執行順序不是先後順序,而是另有規定?事實上,按照同步和異步的劃分方式,並不許確。

因此這裏咱們首先知道了js裏的一種分類方式,就是將任務分爲:同步任務和異步任務。

叨叨了半天,原來都是一些「假大空」的東西,搞什麼搞嘛,哼!!!

準確的劃分方式是:

  • macro-task(宏任務):包括總體代碼script、setTimeout、setInterval
  • micro-task(微任務):Promise、process.nextTick

圖片描述

按照這種分類方式:js的執行機制就是

執行一個宏任務,執行過程當中若是遇到微任務,就將其放在微任務的event queue裏
當前宏任務執行完成後,會查看微任務的event queue,並將裏面的所有微任務依次執行完。
重複以上兩個步驟,結合Event Loop第一課和Event Loop第二課,就是更爲準確的js執行機制了。
嘗試按照剛纔學到的執行機制,去分析第二個栗子:

setTimeout(function(){              
    console.log("定時器開始啦~~~");
})
new Promise(function(resolve){     
    console.log("立刻執行for循環啦");
    for(var i=0;i<10000;i++){
        i==99&&resolve();
    }
}).then(function(){               
    console.log("執行then函數啦")    
});
console.log("表演完畢!");              
//首先執行script中的宏任務;
//遇到setTimeout,放進宏任務的event queue中;
//遇到new Promise直接執行,打印「立刻執行for循環啦」;
//遇到then方法,是微任務,被放進微任務的event queue
//執行打印「表演完畢!」,本輪宏任務執行完畢;
//查看本輪微任務,發現then方法裏的函數,執行打印「執行then函數啦」
//到此,本輪Event Loop所有完成
//下一輪循環裏,先執行一個宏任務,發現宏任務的event queue中有一個setTimeout中的函數,打印「定時器開始啦」

運行結果是:【立刻執行for循環啦----表演完畢!----執行then函數啦----定時器開始啦~~~】

4.談談setTimeout

下面這段setTimeout代碼是什麼意思?咱們通常說:「3s後,會執行setTimeout裏的函數」

setTimeout(function(){
    console.log("執行了");
},3000)

可是這種說法並不嚴謹,準確的解釋是:
        3s後,setTimeout裏的函數會被推入到event queue中,而event queue(事件隊列)裏的任務,只有在主線程空閒時纔會執行。
因此:只有在知足

  1. 3s後
  2. 主線程空閒

這兩個條件同時知足,纔會在3s後執行該函數。若是主線程執行內容不少,執行時間超過3s,好比執行了10s,那麼這個函數只有在10s後執行啦


到此,js引擎的執行機制解讀完啦,前端的水很深,慢慢摸着吧感謝文章的提供者:ziwei3749

相關文章
相關標籤/搜索