我不應動你的,Event Loops(深坑)

我不應動你的,Event Loops

寫在前面的話

本意是想好好研究下 Event Loops, 看了幾篇博客後,才意識到做爲前端打字員的我有多無知,這坑忒深了。javascript

macrotask?,microtask?,MutationObserver? 這些都是啥?規範還沒寫清楚?不一樣瀏覽器運行的還未必同樣?html

但爲了使本身養成常常總結的習慣,仍是寫點什麼吧。前端

故事的開始

<body>
  <script>
    const fib = n => {
      if (n <= 1) {
        return 1
      } else {
        return fib(n - 1) + fib(n - 2)
      }
    }
    console.now = (text) => {
      console.log(`${text} ${new Date().toLocaleString()}`)
    }
    setTimeout(function () {
      console.now(0)
      document.getElementsByTagName('body')[0].style.backgroundColor = 'red'
      console.now(1)
      fib(45)
      console.now(2)
    }, 1000)
  </script>
</body>

計算 fib(45) 是一個至關耗時的工做,在個人chrome里約須要15s左右。html5

問,頁面何時會變成紅色?在執行 console.now(1) 以前就變成紅色了嗎?java

e01

能夠看到即便在 console.now(1) 執行以後,頁面仍舊沒有變紅。git

關於這個現象,能夠有兩種解釋:github

  1. document.getElementsByTagName('body')[0].style.backgroundColor = 'red' 被看成一個異步事件,做爲一個 task,被添加到 event loops
  2. 渲染引擎要等到 JS 引擎空閒時纔開始工做

究竟是哪種?因此將上述代碼修改下web

<script>
    const fib = n => {
      if (n <= 1) {
        return 1
      } else {
        return fib(n - 1) + fib(n - 2)
      }
    }
    console.now = (text) => {
      console.log(`${text} ${new Date().toLocaleString()}`)
    }
    setTimeout(function () {
      console.now(0)
      document.getElementsByTagName('body')[0].style.backgroundColor = 'red'
      console.now(1)
      fib(45)
      console.now(2)
    }, 1000)
    setTimeout(function () {
      console.now(3)
      fib(45)
      console.now(4)
    }, 1000)
  </script>

又增長了一個 setTimeout。這樣的話,若是是第一種解釋,應該在 console.now(3) 運行以前,頁面就變成了紅色;不然就應該採起第二種解釋。chrome

運行結果以下,api

e02

能夠看到在 console.now(3) 以後,頁面依舊沒有變色,看來就是渲染引擎要等到JS引擎徹底空閒時才工做。

事情就這樣結束了嗎

沒有,直到我看到文檔

An event loop must continually run through the following steps for as long as it exists:

  1. Let oldestTask be the oldest task on one of the event loop's task queues, if any, ignoring, in the case of a browsing context event loop, tasks whose associated Documents are not fully active. The user agent may pick any task queue. If there is no task to select, then jump to the microtasks step below.
  2. Set the event loop's currently running task to oldestTask.
  3. Run oldestTask.
  4. Set the event loop's currently running task back to null.
  5. Remove oldestTask from its task queue.
  6. Microtasks: Perform a microtask checkpoint.
  7. Update the rendering: If this event loop is a browsing context event loop (as opposed to a worker event loop), then run the following substeps.

    …...

這段話第7點的意思,怎麼理解起來像是每執行一次 Event Loops 的 task,最後都會更新視圖。

後來看到從event loop規範探究javaScript異步及瀏覽器更新渲染時機

渲染更新(Update the rendering)會在event loop中的tasks和microtasks完成後進行,但並非每輪event loop都會更新渲染,這取決因而否修改了dom和瀏覽器以爲是否有必要在此時當即將新狀態呈現給用戶。

會不會兩次 setTimeout 被合併了?

<script>
    const fib = n => {
      if (n <= 1) {
        return 1
      } else {
        return fib(n - 1) + fib(n - 2)
      }
    }
    console.now = (text) => {
      console.log(`${text} ${new Date().toLocaleString()}`)
    }
    setTimeout(function setTimeout0() {
      console.now(0)
      document.body.style.backgroundColor = 'red'
      console.now(1)
      fib(45)
      console.now(2)
    }, 1000)
    setTimeout(function setTimeout1() {
      console.now(3)
      fib(45)
      console.now(4)
    }, 1001)
  </script>

e04

這樣調整以後,在運行 console.now(3) 以前,頁面的顏色就變了

這樣看來,就是在每一次task以後就可能會更新視圖,而不是等到JS引擎空閒

在執行完setTimeout0後,Event Loops 中實際上仍有 setTimeout1 待執行,可是瀏覽器先渲染了視圖,再執行了setTimeout ,這就推翻了以前渲染引擎要等到 JS 引擎空閒(Event Loops爲空)時纔開始工做。

同時我懷疑,以前代碼

setTimeout(function () {
      console.now(0)
      document.getElementsByTagName('body')[0].style.backgroundColor = 'red'
      console.now(1)
      fib(45)
      console.now(2)
    }, 1000)
    setTimeout(function () {
      console.now(3)
      fib(45)
      console.now(4)
    }, 1000)

會不會被優化成

setTimeout(function () {
      console.now(0)
      document.getElementsByTagName('body')[0].style.backgroundColor = 'red'
      console.now(1)
      fib(45)
      console.now(2)
      console.now(3)
      fib(45)
      console.now(4)
    }, 1000)

坑深,今天先到這,休息下了

參考資料

相關文章
相關標籤/搜索