我不应动你的,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)

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

参考资料

相关文章
相关标签/搜索