Js 運行機制 event loop


Js - 運行機制 (Even Loop)

Javascript 的單線程 - 引用思否的說法:

JavaScript的一個語言特性(也是這門語言的核心)就是單線程。什么是單線程呢?簡單地說就是同一時間只能做一件事,當有多個任務時,只能按照一個順序一個完成了再執行下一個。

 

那為什么JS是單線程的呢?

  • JS最初被設計用在瀏覽器中,作為瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操作DOM
  • 如果瀏覽器中的JS是多線程的,會帶來很復雜的同步問題
  • 比如,假定JavaScript同時有兩個線程,一個線程在某個DOM節點上添加內容,另一個線程刪除了這個節點,這時瀏覽器應該以哪個線程為准?
  • 所以為了避免復雜性,JavaScript從誕生起就是單線程

為了提高CPU的利用率,HTML5提出Web Worker標准,允許JavaScript腳本創建多個線程,但是子線程完全受主線程控制,且不得操作DOM。所以這個標准並沒有改變JavaScript單線程的本質;

 

任務隊列 Task queue

在Javascript中,所有的任務分為兩類:同步任務和異步任務

同步任務指的是,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行后一個任務;

異步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務可以執行了,該任務才會進入主線程執行。

簡要說明:在這里說到了   ‘主線程’   和    ‘任務隊列’ ,個人簡單理解: 主線程就是 Js 執行的線程 , 任務隊列是異步任務暫時存放的一個事件隊列;

 

在Js執行中,同步任務和異步任務分別進入不同的執行"場所",同步的進入主線程,異步的進入 Event Table 並注冊函數。 

當指定的事情完成時,Event Table  會將這個函數移入 Event Queue (事件隊列)。

主線程內的任務執行完畢為空,會去 Event Queue 讀取對應的函數,進入主線程執行。

上述過程會不斷重復,也就是我們常說的 Event Loop(事件循環)。

 

通過上邊的描述,我們來看一張圖更加清晰的了解  Event Loop (事件循環) 機制

接下來我們看一個例子:

        setTimeout(function(){
            console.log('1')
        });
 
        new Promise(function(resolve){
            console.log('2');
            resolve();
        }).then(function(){
            console.log('3')
        });
 
        console.log('4');       

首先setTimeout 是異步進入 事件隊列,然后 promise 的 then 也是異步 進入事件隊列 ,

那么按照我們上邊說的Js執行機制,先走主線程的同步任務,打印2 ,然后4,緊跟着執行異步任務,也就是任務隊列打印1,隨后是 3 , 所以結果應該是 2,4,1,3,  事實真的是這樣子嘛 ?  接着往下看 : 

 

Js 中的宏任務和微任務 - 略記一下

macro-task(宏任務) :包括整體代碼  script,setTimeout,setInterval

micro-task(微任務)  : Promise,process.nextTick

process.nextTick(callback)類似node.js版的"setTimeout",在事件循環的下一次循環中調用 callback 回調函數)

 

我們上邊的  setTimeout  放到了 event queue 事件隊列里 , promise 的 then 函數 也被放到了 event queue 事件隊列里,然而杯具來了,這兩個 queue 並不是一個隊列;

在 Js  Event Loop 機制中

Promise 執行器中的代碼會被主線程同步調用,但是 promise 的回調函數是基於微任務的

宏任務的優先級高於微任務

每一個宏任務執行完畢都必須將當前的微任務隊列清空

emmmm~~

現在我們回到上邊的例子中,因為 settimeout 是宏任務,雖然先執行的它,但是他被放到了宏任務的 event queue 里面,然后代碼繼續往下檢查看有沒有微任務,檢測到 Promise 的 then 函數把它放入了微任務隊列。等到主線進程的所有代碼執行結束后。先從微任務

queue 里拿回調函數,然后微任務queue空了后再從宏任務的queue拿函數。

所以正確的執行結果當然是:2,4,3,1 ;

 

由此延申一下  事循環-宏任務-微任務  (Event Queue - Macro - Micro )關系圖:

 

最后出一個小試題,看看大家是否真的理解到了 Js 的運行機制

試題借鑒  ssssyoki    答案及分析請前往 ssssyoki 博客。

console.log('1');

setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})

setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM