EventLoop面試必考,你完全會了麼?

2021-03-02 石馬上coding
什麼是進程?

進程(Process)是計算機中的程序關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是作業系統結構的基礎。在早期面向進程設計的計算機結構中,進程是程序的基本執行實體;在當代面向線程設計的計算機結構中,進程是線程的容器。程序是指令、數據及其組織形式的描述,進程是程序的實體。

單進程和多進程

顧名思義就是只有一個進程就是單線程,有超過1個進程就是多進程

什麼是線程?

線程(thread)是作業系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。線程是不能單獨存在的,它是由進程來啟動和管理的

單線程和多線程

一個進程可以包含一個線程的時候就是單線程,包含幾個線程的時候就是多線程。

瀏覽器的進程分類1 個瀏覽器(Browser)主進程:主要負責界面顯示、用戶交互、子進程管理,同時提供存儲等功能。1 個GPU 進程:Chrome 剛開始發布的時候是沒有 GPU 進程的。而 GPU 的使用初衷是為了實現 3D CSS 的效果,只是隨後網頁、Chrome 的 UI 界面都選擇採用 GPU 來繪製,這使得 GPU 成為瀏覽器普遍的需求。最後,Chrome 在其多進程架構上也引入了 GPU 進程。1 個網絡(NetWork)進程:主要負責頁面的網絡資源加載,之前是作為一個模塊運行在瀏覽器進程裡面的,直至最近才獨立出來,成為一個單獨的進程。多個渲染進程:核心任務是將 HTML、CSS 和 JavaScript 轉換為用戶可以與之交互的網頁,排版引擎 Blink 和 JavaScript 引擎 V8 都是運行在該進程中,默認情況下,Chrome 會為每個 Tab 標籤創建一個渲染進程。出於安全考慮,渲染進程都是運行在沙箱模式下。多個插件進程:主要是負責插件的運行,因插件易崩潰,所以需要通過插件進程來隔離,以保證插件進程崩潰不會對瀏覽器和頁面造成影響。事件循環(Event Loop)

每個渲染進程都有一個主線程,並且主線程非常繁忙,既要處理 DOM,又要計算樣式,還要處理布局,同時還需要處理 JavaScript 任務以及各種輸入事件。要讓這麼多不同類型的任務在主線程中有條不紊地執行,這就需要消息隊列和事件循環系統來統籌調度這些任務。

單線程處理安排好的任務

我們知道JS是單線程的,一般的代碼都是按順序執行的,也就是已知的安排好的任務都是按順序執行的,等這個任務執行完成之後就會推出線程。

單線程處理安排好的任務

一般情況肯定沒有我們想的那麼好,全都是已知的任務,有可能中間插入其他任務需要執行,這個時候應該怎麼辦呢?

在線程運行中處理新任務

我們可以加一個循環,等待事件進入,然後再執行

在線程運行中處理新任務處理其他線程發送過來的任務

剛剛一直都在討論主線程上的任務執行,那如果有其他線程的任務發給主線程,這個時候主線程怎麼處理呢?

處理其他線程發送過來的任務消息隊列

消息隊列是一種數據結構,可以存放要執行的任務。數據結構裡的隊列就是「先進先出」的特性,一般添加任務就是添加到隊列尾部,然後從隊列的頭部取出任務來操作。

在這裡插入圖片描述消息隊列+循環

添加了一個消息隊列後,其他的線程發送過來的事件就添加到消息隊列的尾部,然後主線程會循環的從消息隊列的頭部取出任務再執行任務。

消息隊列+循環處理其他進程發送過來的任務

剛剛我們是處理其他線程發送給主線程的任務,現在是處理其他進程發送過來的任務,一字之差,其實步驟差不多的。整個渲染進程會有一個IO線程來接收其他進程發送過來的任務,然後再把接收到的其他進程任務添加到消息隊列的尾部,渲染主線程就循環的取出任務,執行任務。

處理其他進程發送過來的任務消息隊列中的任務類型macro-task(宏任務)包括整體代碼script,setTimeout,setIntervalmicro-task(微任務)Promise的then,await 的下一行開始,process.nextTick(類似node.js版的"setTimeout")requestAnimationFrame(RAF)window.requestAnimationFrame() 告訴瀏覽器——你希望執行一個動畫,並且要求瀏覽器在下次重繪之前調用指定的回調函數更新動畫。該方法需要傳入一個回調函數作為參數,該回調函數會在瀏覽器下一次重繪之前執行。它是由系統來決定回調函數的執行時機的,會請求瀏覽器在下一次重新渲染之前執行回調函數。無論設備的刷新率是多少,requestAnimationFrame 的時間間隔都會緊跟屏幕刷新一次所需要的時間;例如某一設備的刷新率是 75 Hz,那這時的時間間隔就是 13.3 ms(1 秒 / 75 次)。需要注意的是這個方法雖然能夠保證回調函數在每一幀內只渲染一次,但是如果這一幀有太多任務執行,還是會造成卡頓的;因此它只能保證重新渲染的時間間隔最短是屏幕的刷新時間。

具體還是可以看MDN:requestAnimationFrame

requestIdleCallbackwindow.requestIdleCallback()方法將在瀏覽器的空閒時段內調用的函數排隊。這使開發者能夠在主事件循環上執行後臺和低優先級工作,而不會影響延遲關鍵事件,如動畫和輸入響應。函數一般會按先進先調用的順序執行,然而,如果回調函數指定了執行超時時間timeout,則有可能為了在超時前執行函數而打亂執行順序。你可以在空閒回調函數中調用requestIdleCallback(),以便在下一次通過事件循環之前調度另一個回調。和requestAnimationFrame 每一幀必定會執行不同,requestIdleCallback 是撿瀏覽器空閒來執行任務。

具體還是可以看MDN:requestIdleCallback

事件循環,宏任務,微任務的關係遇到promise.then等微任務的時候會放進微任務隊列尾部遇到setTimeout,setInterval 等宏任務的時候會放入宏任務的隊列尾部最大的宏任務執行完後,查看有沒有微任務,有的話執行微任務,沒有的話執行下一個宏任務執行setTimeout,setInterval 等宏任務的時候,如果裡面有promise.then等代碼的時候又要放入微任務隊列尾部,執行完setTimeout,setInterval 後查看有沒有要執行的微任務,沒有的話執行下一個宏任務事件循環,宏任務,微任務的關係requestAnimationFrame、requestIdleCallback、事件循環、宏任務,微任務的關係

raf 在 render 中,requestIdleCallback 在 render 之後。raf在渲染之前執行的,requestIdleCallback 是在瀏覽器空閒時間才會執行,所以requestIdleCallback是最後執行的。

RAF、RIC、事件循環、宏任務,微任務的關係

可以用這個代碼體驗一下執行順序

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #animation{
      background-color: aqua;
      width: 100px;
      height: 100px;
      border:1px solid rebeccapurple;
    }
  </style>
</head>
<body>
  <div id='animation'>animation</div>

  <script>

window.requestIdleCallback(myNonEssentialWork);
const tasks = [
 () => {
   console.log("第一個任務");
 },
 () => {
   console.log("第二個任務");
 },
 () => {
   console.log("第三個任務");
 },
];

function myNonEssentialWork (deadline) {
  // 如果幀內有富餘的時間,或者超時
  while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && tasks.length > 0) {
    work();
  }

 if (tasks.length > 0) window.requestIdleCallback(myNonEssentialWork);

}

function work () {
 tasks.shift()();
//  console.log('執行任務');
}

var start = null;
var element = document.getElementById('animation');
element.style.position = 'absolute';

function step(timestamp) {
  console.log('222')
  if (!start) start = timestamp;
  var progress = timestamp - start;
  element.style.left = Math.min(progress / 10, 200) + 'px';
  // if (progress < 2) {
  //   window.requestAnimationFrame(step);
  // }
}
window.requestAnimationFrame(step);

setTimeout(function() {
  console.log('setTimeout');
})

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

console.log('console');

  </script>
</body>
</html>

Chrome瀏覽器的執行順序是

可以看到出現兩種情況,requestAnimationFrame列印出來的222會在setTimeout之前或者之後,是因為raf是由系統來決定回調函數的執行時機的,會請求瀏覽器在下一次重新渲染之前執行回調函數,這個下一次重新渲染的時機我們不能固定,所以列印出來的順序是不固定的。

舉個🌰
setTimeout(function() {
  console.log('setTimeout1');
})

new Promise(function(resolve) {
  console.log('promise');
  resolve();
}).then(function() {
  console.log('then');
})
setTimeout(function() {
  console.log('setTimeout2');
})

async function aaa() {
  console.log('async1')
  return 'async2'
}

async function test () {
  let a = await aaa();
  console.log('await',a)
}
test()
console.log('console');

這個代碼列印出來的順序你知道嗎?

這一整塊代碼就是一個宏任務,先一行一行代碼看,看到一個setTimeout,先把它放到宏任務隊列的尾部標記為setTimeout1然後遇到new Promise 這個時候就是立刻執行了,所以先輸出「promise」再往下,又遇到了setTimeout,再把它放到宏任務隊列的尾部標記為setTimeout2,這個時候宏隊列是這樣的:【setTimeout2】【setTimeout1】遇到了test的函數調用,就直接執行test內部,遇到await也是直接執行aaa(),所以輸出「async1」,await這行後面的代碼都是放入微任務隊列尾部,這個時候微任務隊列長這樣:【await async2】【then】遇到最後一行代碼了,這個時候直接輸出「console」,至此最大的宏任務執行完畢。這個時候查看有沒有微任務,我們看到微任務隊列【await async2】【then】,然後從隊頭取出微任務,所以依次輸出thenawait async2微任務隊列為空後,說明執行完畢,然後查看有沒有下一個宏任務我們從宏任務隊列的頭部取出「setTimeout1」,輸出「setTimeout1」,這個宏任務裡面並沒有微任務,所以這個setTimeout1宏任務結束開始新的setTimeout2宏任務,**輸出「setTimeout2」**後,這個宏任務裡面並沒有微任務,所以這個setTimeout2宏任務結束。至此沒有宏任務也沒有微任務了,所以就結束了。依次輸出的結果是結果小練習

看看你有沒有掌握,下面幾個🌰輸出的結果是什麼呢?

PS:process.nextTick要在node環境才能運行出來

console.log('1');

process.nextTick(function() {
    console.log('6');
})

new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})

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


(async () => {

  console.log('1');
  await new Promise((resolve, reject) => {

    console.log('2');

    setTimeout(() => {

      console.log('3')
    }, 0)
    console.log('4')

  })
  console.log('5')
})();


1、李兵老師的瀏覽器工作原理與實踐(這是一個付費專欄,以下連結不屬於這個專欄) 2、https://juejin.im/post/6844903512845860872 3、https://www.jianshu.com/p/2771cb695c81 4、https://html.spec.whatwg.org/multipage/webappapis.html#event-loops 5、https://javascript.info/event-loop

相關焦點

  • Event Loop淺談
    event loop 即事件循環。最初了解到js的event loop機制是通過自己對js中異步、同步的疑惑。
  • 你不知道的 Event Loop
    筆者最近忙著做項目之類的,文章輸出遺落下了一段時間,這次我們就來聊一個面試中一個比較重要的知識點 —— Event Loop可能有人會奇怪一個 EventLoop 還能寫出什麼,且聽我慢慢來逼叨,看完這篇文章帶你搞定 Event Loop 以及它相關的一些知識點。
  • 如何解釋Event Loop面試官才滿意?
    但是因為它創建的子線程完全受控於主線程,且位於外部文件中,無法訪問DOM。所以它並沒有改變js單線程的本質。單線程就意味著,所有的任務都需要排隊。就像還不能自助點餐的時候你去肯德基需要排隊,有的人沒想好點什麼或者點的東西很多,耗時就會長,那麼後面的人也只好排隊等待。有了自助點餐服務後,一切問題迎刃而解。
  • 深入理解 Event Loop
    本文就瀏覽器與nodejs環境下異步實現與event loop進行相關解釋。瀏覽器環境瀏覽器環境下,會維護一個任務隊列,當異步任務到達的時候加入隊列,等待事件循環到合適的時機執行。Task 執行完畢之後檢查清空,而這次 event-loop 的新 task 會在下次 event-loop 檢測。
  • 一篇文章教會你 Event loop——瀏覽器和 Node
    意義在實際工作中,了解Event loop的意義能幫助你分析一些異步次序的問題(當然,隨著es7 async和await的流行,這樣的機會越來越少了)。除此以外,它還對你了解瀏覽器和Node的內部機制有積極的作用;對於參加面試,被問到一堆異步操作的執行順序時,也不至於兩眼抓瞎。3.
  • 一次弄懂Event Loop
    應對各大網際網路公司的面試,懂其原理,題目任其發揮。堆,棧、隊列堆(Heap)堆是一種數據結構,是利用完全二叉樹維護的一組數據,堆分為兩種,一種為最大堆,一種為最小堆,將根節點最大的堆叫做最大堆startendpromise3timer1timer2promise1promise2具體詳情可以查看《又被node的eventloop坑了,這次是node的鍋》(https://juejin.im/post/5c3e8d90f265da614274218a)。
  • 面試必考:你真的理解 $nextTick 麼
    「微任務」的響應速度相比setTimeout(下一個「宏任務」)會更快,因為無需等待UI渲染。當前「宏任務」執行後,會將在它執行期間產生的所有「微任務」都執行一遍。Once the current turn of the event loop turn runs to completion, all callbacks currently in the next tick queue will be called.This is not a simple alias to setTimeout(fn, 0).
  • 【小心得】淺析Nodejs Event Loop
    每一個階段都有一個裝有callbacks的fifo queue(隊列),當event loop運行到一個指定階段時,node將執行該階段的fifo queue(隊列),當隊列callback執行完或者執行callbacks數量超過該階段的上限時,event loop會轉入下一下階段.
  • 面試官:什麼是 EventLoop.你:一臉蒙蔽.看完這篇文章就懂了
    面試官:什麼是 EventLoop。你:一臉蒙蔽。看完這篇文章就懂了文章翻譯自:https://javascript.info/event-loop在這片文章,我們要帶著兩個問題去學習事件循環瀏覽器 js 以及
  • jsliang 求職系列 - 06 - Event Loop
    然後因為單線程只能先讓前面的程序走完,即便這個接口或者圖片緩過來了,我下面還有其他任務沒做呢,這不就卡死了麼?所以這時候異步來了:在涉及某些需要等待的操作的時候,我們就選擇讓程序繼續運行。簡單來說:你的頁面放到了瀏覽器去展示,你的數據放到了後臺處理(將 Node.js 看成 PHP、Java 等後端語言),這兩者能沒有區別麼?!
  • 【第1790期】圖解Event Loop
    當我們調用一個函數時,函數會被放入一個叫做調用棧(call stack,也叫執行上下文棧)的地方。調用棧是JS引擎的一部分,並非瀏覽器特有的。調用棧是一個棧數據結構,具有後進先出的特點(Last in, first out. LIFO)。當函數執行完畢返回時,會被彈出調用棧。
  • Event Loop的規範和實現
    小測試(1)先來看一段代碼,列印結果會是?而真正痛過的同學會告訴你,答案是:1、0、2。並且,無論是chrome還是node下的運行結果都是一致的。(錯誤訂正:經多次驗證,node下的輸出順序依然是無法保證的,node的timer真是一門玄學~)Chrome中的timer從測試(3)結果可以看出,0ms和1ms的延時效果是一致的,那背後的原因是為什麼呢?我們先查查blink的實現。
  • 前端最慘面經,你不會比我更慘
    這題聊起來可就大了,進程,線程,協程。部分還會配以那道最經典的eventloop題目。React 16 以後,有些鉤子函數會執行多次,這是因為引入 Fiber 的原因,這在後續的章節中會講到。React 需要使用 JSX,有一定的上手成本,並且需要一整套的工具鏈支持,但是完全可以通過 JS 來控制頁面,更加的靈活。Vue 使用了模板語法,相比於 JSX 來說沒有那麼靈活,但是完全可以脫離工具鏈,通過直接編寫 render 函數就能在瀏覽器中運行。
  • Power BI 有哪些面試必考知識點?
    LIVE #2: 4/28 用過無數商業智能工具和數據可視化的你了解 PowerBI 麼?PowerBI是什麼?Power BI 是交互式數據可視化BI 工具,可以讓你以全新方式查看公司的數據。它的核心理念就是讓沒有很強大技術背景的用戶也可以輕輕鬆鬆地進行數據分析及可視化。那麼作為一名合格的數據人,你會使用 PowerBI 麼?你了解它的魅力和作用麼?
  • 一次弄懂Event Loop(徹底解決此類面試問題)
    應對各大網際網路公司的面試,懂其原理,題目任其發揮。堆,棧、隊列堆(Heap)堆是一種數據結構,是利用完全二叉樹維護的一組數據,堆分為兩種,一種為最大堆,一種為最小堆,將根節點最大的堆叫做Node的Event loop一共分為6個階段,每個細節具體如下:timers: 執行setTimeout和setInterval中到期的callback。
  • 深入解析 EventLoop 和瀏覽器渲染、幀動畫、空閒回調的關係
    進入更新渲染階段,判斷是否需要渲染,這裡有一個 rendering opportunity 的概念,也就是說不一定每一輪 event loop 都會對應一次瀏覽 器渲染,要根據屏幕刷新率、頁面性能、頁面是否在後臺運行來共同決定,通常來說這個渲染間隔是固定的。
  • EventLoop 系列 - 聊聊 Node.js 中的事件循環
    下圖展示了它的組成部分,Network I/O 是網絡處理相關的部分,右側還有文件操作、DNS,底部 epoll、kqueue、event ports、IOCP 這些是底層不同作業系統的實現。但是會破壞事件循環調度,setTimeout 將永遠得不到執行。
  • Twitch數據科學家手把手教你拿下面試必考的SQL
    >子查詢(Subqueries)和公共表表達式(Common Table Expressions)現在你知道如何檢索一組行和列,這可能會讓你通過面試官的第一個問題。,完全沒關係——熟能生巧。Case語句則是另一個相當簡單的概念,與R和Excel等軟體中的ifelse函數完全相同,通常應用於將一組預定義值映射到另一組預定義值的情況。例如,你可能希望將星期幾所在的列轉換為另一個變量,代表該天是否為周末。
  • 事件循環Event Loop
    那麼問題來了,假如我們想瀏覽新聞,但是新聞包含的超清圖片加載很慢,難道我們的網頁要一直卡著直到圖片完全顯示出來?因此聰明的程式設計師將任務分為兩類:1)同步任務 2)異步任務#1.同步和異步任務分別進入不同的執行"場所",同步的進入主線程,異步的進入Event Table並註冊函數。
  • 瀏覽器和 Node.js 的 EventLoop 為什麼這麼設計?
    Event Loop 是 JavaScript 的基礎概念,面試必問,平時也經常談到