一道關於JavaScript 代碼執行順序的面試題解析


javascript

0. 引言:

最近寫了一些異步遞歸的代碼,着實有點頭疼,索性重新研究一下JavaScript 代碼執行順序,並附上一道面試題的解析。

1. JavaScript 代碼執行順序

首先我們了解幾個概念

1.1 微任務/宏任務

異步隊列中包括:微任務(micro-task) 和 宏任務(macro-task)

微任務包括: process.nextTickPromiseprocess.nextTick 為 Node 獨有)

宏任務包括: scriptsetTimeoutsetIntervalsetImmediateI/OUI rendering

Tips:

  • 微任務優先級高於宏任務的前提是:同步代碼已經執行完成。因為 script 屬於宏任務,程序開始后會首先執行同步腳本,也就是script
  • Promise 里邊的代碼屬於同步代碼,.then() 中執行的代碼才屬於異步代碼。

1.2 Event Loop(事件輪詢)

Event Loop 是一個程序結構,用於等待和發送消息和事件。

Event Loop 執行順序如下所示:

  • 首先執行同步代碼(宏任務)
  • 當執行完所有同步代碼后,執行棧為空,查詢是否有異步代碼需要執行
  • 執行所有微任務
  • 當執行完所有微任務后,如有必要會渲染頁面
  • 然后開始下一輪 Event Loop,執行宏任務中的異步代碼,也就是 setTimeout 中的回調函數

Tips:簡化講:先執行一個宏任務(script同步代碼),然后執行並清空微任務,再執行一個宏任務,然后執行並清空微任務,再執行一個宏任務,再然后執行並清空微任務......如此循環往復(一個宏任務 -> 清空微任務 -> 一個宏任務 -> 清空微任務)

javascript代碼執行順序

2. 面試題詳解

2.1 題目

setTimeout(function () {
  console.log(" set1");
  new Promise(function (resolve) {
    resolve();
  }).then(function () {
    new Promise(function (resolve) {
      resolve();
    }).then(function () {
      console.log("then4");
    });
    console.log("then2 ");
  });
});

new Promise(function (resolve) {
  console.log("pr1");
  resolve();
}).then(function () {
  console.log("then1");
});

setTimeout(function () {
  console.log("set2");
});

console.log(2);

new Promise(function (resolve) {
  resolve();
}).then(function () {
  console.log("then3");
});

2.2 執行過程解析

執行所有同步代碼(第一次宏任務):

setTimeout(function () { // setTimeout 內 function 放入宏任務
  console.log(" set1");
  new Promise(function (resolve) {
    resolve();
  }).then(function () {
    new Promise(function (resolve) {
      resolve();
    }).then(function () {
      console.log("then4");
    });
    console.log("then2 ");
  });
});

new Promise(function (resolve) {
  console.log("pr1"); // Promise里邊的代碼直接執行  打印 pr1
  resolve();
}).then(function () {
  console.log("then1"); // Promise.then 放入微任務
});

setTimeout(function () {
  console.log("set2"); // setTimeout內function 放入宏任務
});

console.log(2); // 打印 2

new Promise(function (resolve) {
  resolve();
}).then(function () {
  console.log("then3"); //Promise.then 放入微任務
});


// 此時控制台打印 : pr1  >  2
// 異步任務隊列:[微任務數:2][宏任務數:2]
// 執行並清空微任務

執行並清空微任務

function () {
  console.log("then1");  // 輸出 then1
}

function () {
  console.log("then3"); // 輸出 then3
}

// 此時控制台打印 : then1  >  then3
// 異步任務:[微任務數:0][宏任務數:2]
// 執行一個宏任務

執行一個宏任務

function () {
  console.log(" set1");   //打印 set1
  new Promise(function (resolve) {
    resolve();
  }).then(function () {     // Promise.then 放入微任務
    new Promise(function (resolve) {
      resolve();
    }).then(function () {
      console.log("then4");
    });
    console.log("then2 ");
  });
}

// 此時控制台打印 : set1
// 異步任務:[微任務數:1][宏任務數:1]
// 執行並清空微任務

執行並清空微任務

function () {     
    new Promise(function (resolve) {
      resolve();      
    }).then(function () {
      console.log("then4");   // Promise.then 放入微任務
    });
    console.log("then2 ");    // 打印 then2
}

// 此時控制台打印 : then2
// 異步任務:[微任務數:1][宏任務數:1]
// 此時微任務列表增加並未清空,繼續執行微任務

此時微任務列表增加並未清空,繼續執行微任務

function () {
      console.log("then4");   // 打印 then4
}

// 此時控制台打印 : then4
// 異步任務:[微任務數:0][宏任務數:1]
// 執行宏任務

執行宏任務

function () {
  console.log("set2"); // 打印 set2
}
// 此時控制台打印 : set2
// 異步任務:[微任務數:0][宏任務數:0]
// 程序結束

完整輸入順序

pr1
2
then1
then3
set1
then2 
then4
set2

推薦閱讀

如果對你有一點點幫助,可以點喜歡點贊點收藏。

您的關注是莫大的鼓勵 ❥(^_-)


免責聲明!

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



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