[面試題]事件循環經典面試題解析


基礎概念

  1. 進程是計算機已經運行的程序,線程是操作系統能夠進行運算調度的最小單位,它被包含在進程中.瀏覽器中每開一個Tab頁,就會打開一個進程,而這個進程又包含了很多線程.
  2. 大家都知道JS是一門單線程語言,如果遇到了非常耗時的操作,那么JS的執行就會受到阻塞,這肯定不是我們想看到的,所以這些耗時的操作,往往不是由JS線程所執行的,而是交由瀏覽器中的其他線程去完成的,成功之后只要在某個特定的時候進行一個回調函數即可
  3. 所以引出了事件循環的概念,在事件循環中,分兩種任務,分別是宏任務和微任務
    1. 宏任務包含 ajax、setTimeout、setInterval、DOM監聽、UI Rendering
    2. 微任務包含 Promise的then回調、 Mutation Observer API、queueMicrotask()等
  4. 接下來我們直接就開始練習面試題熟悉熟悉

面試題一

setTimeout(function () {
    console.log("setTimeout1");

    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("promise1");
    resolve();
}).then(function () {
    console.log("then1");
});

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

console.log(2);

queueMicrotask(() => {
    console.log("queueMicrotask1")
});

new Promise(function (resolve) {
    resolve();
}).then(function () {
    console.log("then3");
});
  1. 先解決同步任務
    1. 輸出promise1 2
  2. 開始解決異步任務中的微任務
    1. 輸出then1 queueMicrotask1 then3
  3. 開始解決異步任務中的宏任務
    1. 輸出setTimeout1,在第一個定時器中,又遇到了微任務,那么接着執行微任務
      1. 輸出then2 然后輸出 then4
    2. 目光跳出第一個定時器中,看到第二個定時器 開始輸出setTimeout2
  4. 最后的完整輸出為 promise1 2 then1 queueMicrotask1 then3 setTimeout1 then2 then4 setTimeout2

面試題二

async function async1() {
  console.log('async1 start')
  // await異步函數的返回結果 resolve的結果會作為整個異步函數的promise的resolve結果->同步代碼
  // await后面的執行代碼 就會變成.then后面的執行函數->微任務
  // 也就是說  console.log('async1 end') 這一段是相當於then方法內的 會被加入微任務中
  await async2();
  console.log('async1 end')
}

async function async2() {
  console.log('async2')
}

console.log('script start')

setTimeout(function () {
  console.log('setTimeout')
}, 0)

async1();

new Promise(function (resolve) {
  console.log('promise1')
  resolve();
}).then(function () {
  console.log('promise2')
})

console.log('script end')
  1. 先執行同步代碼
    1. 輸出script start async1 start async2 promise1 script end
  2. 開始執行微任務
    1. 輸出async1 end promise2
  3. 最后執行宏任務
    1. 輸出setTimeout
  4. 完整輸出:script start async1 start async2 promise1 script end async1 end promise2 setTimeout

面試題三

Promise.resolve().then(() => {
  console.log(0);
  //1.直接返回4 微任務不會做任何延遲 
  // return 4
  //2.直接返回Promise.resolve(4) 微任務推遲兩次
  // return Promise.resolve(4);
  //3.返回thenable對象
  return {
    then: ((resolve, reject) => {
      resolve(4);
    })
  }
}).then((res) => {
  console.log(res)
})

Promise.resolve().then(() => {
  console.log(1);
}).then(() => {
  console.log(2);
}).then(() => {
  console.log(3);
}).then(() => {
  console.log(5);
}).then(() => {
  console.log(6);
})

這道面試題有些特殊,需要大家記住兩個結論

  1. 如果返回的是thenable對象,那么微任務會推遲一次,thenable對象就是實現了Promise.then的那個函數,具體可看代碼
  2. 如果返回的是Promise.resolve(4),那么微任務會推遲兩次,這個相當於是返回一個Promise之后又用了resolve,二者是等價的
  3. 為了配合大家理解,我給大家畫了幾張圖,大家可以看看

image.png
事件循環_return.gif事件循環_return_thenable_.gif事件循環_return_promise.resolve_.gif

面試題四

本道題是基於node的事件循環,和瀏覽器的事件循環不一樣,需要記住以下幾點

node的事件循環也分宏任務和微任務

  • 宏任務: setTimeout、setInterval、IO事件、setImmediate、close事件
  • 微任務: Promise的then回調、process.nextTick、queueMicrotask

node的每次事件循環都是按照以下順序來執行的

  1. next tick microtask queue
  2. other microtask queue
  3. timer queue
  4. poll queue
  5. pcheck queue
  6. close queue
async function async1() {
  console.log('async1 start')
  await async2()
  console.log('async1 end')
}

async function async2() {
  console.log('async2')
}

console.log('script start')

setTimeout(function () {
  console.log('setTimeout0')
}, 0)

setTimeout(function () {
  console.log('setTimeout2')
}, 300)

setImmediate(() => console.log('setImmediate'));

process.nextTick(() => console.log('nextTick1'));

async1();

process.nextTick(() => console.log('nextTick2'));

new Promise(function (resolve) {
  console.log('promise1')
  resolve();
  console.log('promise2')
}).then(function () {
  console.log('promise3')
})

console.log('script end')
  1. 首先執行同步任務
    1. 輸出script start async1 start async2 promise1 promise2 script end
  2. 接着執行微任務,因為node會優先執行nextTick這個微任務
    1. 所以先輸出nextTick1 nextTick2
    2. 在輸出其他微任務,輸出async1 end promise3
  3. 最后執行宏任務
    1. 輸出 setTimeout0 setImmediate
    2. 因為這個定時器延時3ms執行,所以會讓其他的宏任務先執行完畢,才回去執行這個定時器,所以最后輸出setTimeout2
  4. 最后的輸出結果: script start async1 start async2 promise1 promise2 script end nextTick1 nextTick2 async1 end promise3 setTimeout0 setImmediate setTimeout2


免責聲明!

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



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