小白理解 JavaScript 執行機制

1.JavaScript爲何是單線程?

JavaScript語言的一大特色就是單線程,也就是說,同一個時間只能作一件事。那麼,爲何JavaScript不能有多個線程呢?這樣能提升效率啊。
JavaScript的單線程,與它的用途有關。做爲瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操做DOM。這決定了它只能是單線程,不然會帶來很複雜的同步問題。好比,假定JavaScript同時有兩個線程,一個線程在某個DOM節點上添加內容,另外一個線程刪除了這個節點,這時瀏覽器應該以哪一個線程爲準?javascript

因此,爲了不復雜性,從一誕生,JavaScript就是單線程,這已經成了這門語言的核心特徵,未來也不會改變。
爲了利用多核CPU的計算能力,HTML5提出Web Worker標準,容許JavaScript腳本建立多個線程,可是子線程徹底受主線程控制,且不得操做DOM。因此,這個新標準並無改變JavaScript單線程的本質。html

二、執行機制相關知識點

2.1同步任務、異步任務

同步任務
異步任務前端

有一天,張三要去作飯 這時候他要作兩件事 分別是蒸米飯 和 抄菜 ,如今有兩種方式去完成這個任務java

A.先去蒸米飯  而後等蒸米飯好了 再去抄菜           ---同步任務
   B.先去蒸米飯  而後等蒸米飯的過程當中 再去抄菜        ---異步任務

其實這兩個方式不就是 一種是同步任務(synchronous),另外一種是異步任務(asynchronous)嗎。同步任務指的是,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;異步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行。
當咱們打開網站時,網頁的渲染過程就是一大堆同步任務,好比頁面骨架和頁面元素的渲染。而像加載圖片音樂之類佔用資源大耗時久的任務,就是異步任務。,咱們用導圖來講明:node

具體來講,異步執行的運行機制以下。(同步執行也是如此,由於它能夠被視爲沒有異步任務的異步執行。)面試

(1)全部同步任務都在主線程上執行,造成一個執行棧(execution context stack)。
(2)主線程以外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。
(3)一旦"執行棧"中的全部同步任務執行完畢,系統就會讀取"任務隊列",看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。
(4)主線程不斷重複上面的第三步。ajax

2.三、JavaScript的宏任務與微任務

  除了廣義上的定義,咱們能夠將任務進行更精細的定義,分爲宏任務與微任務:瀏覽器

宏任務(macro-task): 包括總體代碼script腳本的執行,setTimeout,setInterval,ajax,dom操做 還有如 I/O 操做、UI 渲 染等bash

微任務(micro-task): Promise回調 node 中的 process.nextTick 、對 Dom 變化監聽的 MutationObserver。dom

主線程都從"任務隊列"中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲 Event Loop(事件循環)

咱們解釋一下這張圖:

同步和異步任務分別進入不一樣的執行"場所",同步的進入主線程,異步的進入Event Table並註冊函數。
當指定的事情完成時,Event Table會將這個函數移入Event Queue。
主線程內的任務執行完畢爲空,會去Event Queue讀取對應的函數,進入主線程執行。
上述過程會不斷重複,也就是常說的Event Loop(事件循環)。(Event Loop是javascript的執行機制)

三、總結

3.1面試回答

面試中該如何回答呢? 下面是我我的推薦的回答:

首先js 是單線程運行的,在代碼執行的時候,經過將不一樣函數的執行上下文壓入執行棧中來保證代碼的有序執行。
在執行同步代碼的時候,若是遇到了異步事件,js 引擎並不會一直等待其返回結果,而是會將這個事件掛起,繼續執行執行棧中的其餘任務
當同步事件執行完畢後,再將異步事件對應的回調加入到與當前執行棧中不一樣的另外一個任務隊列中等待執行。
任務隊列能夠分爲宏任務對列和微任務對列,噹噹前執行棧中的事件執行完畢後,js 引擎首先會判斷微任務對列中是否有任務能夠執行,若是有就將微任務隊首的事件壓入棧中執行。
當微任務對列中的任務都執行完成後再去判斷宏任務對列中的任務。

3.2優先級

3.2.1 setTimeout()、setInterval()

setTimeout() 和 setInterval() 這兩個函數,它們的內部運行機制徹底同樣,區別在於前者指定的代碼是一次性執行,後者則爲反覆執行。
setTimeout() 和 setInterval() 產生的任務是 異步任務,也屬於 宏任務。
  setTimeout() 接受兩個參數,第一個是回調函數,第二個是推遲執行的毫秒數。setInterval() 接受兩個參數,第一個是回調函數,第二個是反覆執行的毫秒數。
  若是將第二個參數設置爲0或者不設置,意思 並非當即執行,而是指定某個任務在主線程最先可得的空閒時間執行,也就是說,儘量早得執行。它在"任務隊列"的尾部添加一個事件,所以要等到同步任務和"任務隊列"現有的事件都處理完,纔會獲得執行。
 因此說,setTimeout() 和 setInterval() 第二個參數設置的時間並非絕對的,它須要根據當前代碼最終執行的時間來肯定的,簡單來講,若是當前代碼執行的時間(如執行200ms)超出了推遲執行(setTimeout(fn, 100))或反覆執行的時間(setInterval(fn, 100)),那麼setTimeout(fn, 100) 和 setTimeout(fn, 0) 也就沒有區別了,setInterval(fn, 100) 和 setInterval(fn, 0) 也就沒有區別了。

3.2.2 Promise

Promise 相對來講就比較特殊了,在 new Promise() 中傳入的回調函數是會 當即執行 的,可是它的 then() 方法是在 執行棧以後,任務隊列以前 執行的,它屬於 微任務。

3.2.3 process.nextTick

process.nextTick 是 Node.js 提供的一個與"任務隊列"有關的方法,它產生的任務是放在 執行棧的尾部,並不屬於 宏任務 和 微任務,所以它的任務 老是發生在全部異步任務以前。

3.2.4 setImmediate

setImmediate 是 Node.js 提供的另外一個與"任務隊列"有關的方法,它產生的任務追加到"任務隊列"的尾部,它和 setTimeout(fn, 0) 很像,但優先級都是 setTimeout 優先於 setImmediate。
  有時候,setTimeout 的執行順序會在 setImmediate 的前面,有時候會在 setImmediate 的後面,這並非 node.js 的 bug,這是由於雖然 setTimeout 第二個參數設置爲0或者不設置,可是 setTimeout 源碼中,會指定一個具體的毫秒數(node爲1ms,瀏覽器爲4ms),而因爲當前代碼執行時間受到執行環境的影響,執行時間有所起伏,若是當前執行的代碼小於這個指定的值時,setTimeout 還沒到推遲執行的時間,天然就先執行 setImmediate 了,若是當前執行的代碼超過這個指定的值時,setTimeout 就會先於 setImmediate 執行。

3.2.5 總結優先級 重點重點重點重點重點重點

經過上面的介紹,咱們就能夠得出一個代碼執行的優先級:
同步代碼(宏任務) > process.nextTick > Promise(微任務)> setTimeout(fn)、setInterval(fn)(宏任務)> setImmediate(宏任務)> setTimeout(fn, time)、setInterval(fn, time),其中time>0

4.用實例來檢驗一下學習成果

第一題:

setTimeout(function() {
  console.log(1)   //做爲宏任務,暫不執行,放到任務隊列中
}, 0);
new Promise(function(resolve, reject) {
  console.log(2);  //輸出2
  resolve()
}).then(function() {
  console.log(3)  //then回調函數做爲微任務,暫不執行 放入任務隊列
});
//// 如下代碼須要在 node 環境中執行
process.nextTick(function () {
  console.log(4)  //暫不執行 放入任務隊列
})
console.log(5)    //輸出5

題目解析:
第一輪: setTimeout做爲宏任務,暫不執行,放到任務隊列中
執行new Promise 輸出2 then回調函數做爲微任務,暫不執行 放入任務隊列
繼續執行 碰見process.nextTick一樣將回調函數扔到爲任務隊列,再繼續執行,輸出5
致此同步任務執行完成 根據前面獲得的優先級 咱們接着執行process.nextTick輸出4
尋找任務隊列的微任務 找到then回調函數輸出3 微任務執行完畢
尋找宏任務setTimeout輸出1
結果: 25431
複製代碼
第二題

console.log(1);  //當即輸出1

setTimeout(function () {
    console.log(2);   // 做爲宏任務,暫不執行,放到任務隊列中
    new Promise(function (resolve, reject) {
        console.log(3); 
        resolve();
        console.log(4);
    }).then(function () {
        console.log(5);
    });
});

function fn() {
    console.log(6);
    setTimeout(function () {  // 做爲宏任務,暫不執行,放到任務隊列中
        console.log(7);
    }, 50);
}

new Promise(function (resolve, reject) {
    console.log(8);
    resolve();
    console.log(9);
}).then(function () { // 做爲微任務,暫不執行
    console.log(10);
});

fn();

console.log(11);

// 如下代碼須要在 node 環境中執行
process.nextTick(function () {
    console.log(12);
});

setImmediate(function () {
    console.log(13);
});
複製代碼
此時輸出爲:1 8 9 6 11 12
運行微任務:
new Promise(function (resolve, reject) {
    // console.log(8); // 已執行
    // resolve(); // 已執行
    // console.log(9); // 已執行
})
.then(function () {
    console.log(10);
});
複製代碼
此時輸出爲:10
讀取"任務隊列"的回調函數到"執行棧":
setTimeout(function () {
    console.log(2);

    new Promise(function (resolve, reject) {
        console.log(3);
        resolve();
        console.log(4);
    })
    //.then(function () { // 做爲微任務,暫不執行
    //  console.log(5);
    //});
});

此時輸出爲:2 3 4
再運行微任務:

setTimeout(function () {
    // console.log(2); // 已執行

    new Promise(function (resolve, reject) {
        // console.log(3); // 已執行
        // resolve(); // 已執行
        // console.log(4); // 已執行
    })
    .then(function () {
        console.log(5);
    });
});

此時輸出爲:5
再讀取"任務隊列"的回調函數到"執行棧":

setImmediate(function () {
    console.log(13);
});

此時輸出爲:13
運行微任務:

再讀取"任務隊列"的回調函數到"執行棧":

// function fn() { // 已執行
    // console.log(6); // 已執行
    setTimeout(function () {
        console.log(7);
    }, 50);
// }

此時輸出爲:7
運行微任務:

綜上,最終的輸出順序是:1 8 9 6 11 12 10 2 3 4 5 13 7
看到這裏的同窗 想必應該已經掌握了JavaScript執行機制了吧
但願你們2021 心想事成
我是前端魔法師 一名小白 歡迎你們點贊 感謝你們
參考連接
https://www.cnblogs.com/shcrk...

http://www.ruanyifeng.com/blo...

https://juejin.cn/post/684490...

https://cloud.tencent.com/dev...

https://juejin.cn/post/684490...

相關文章
相關標籤/搜索