• chrome事件循环的自问自答


    chrome事件循环的自问自答

    目录

    1. 宏任务有哪些?

    • 事件回调 (js调用的 click box.click()
    • XHR或网络请求回调
    • 定时器的回调
    • I/O回调
    • history相关回调
    • MessageChannel的message回调

    在合适时机,这些宏任务会被推入宏任务队列;每一次事件循环会从宏任务队列中取一个任务执行。

    history.back回调:

    
    <button id='box2'>backbutton>
    
    <script>
      var box = document.getElementById('box');
      var box2 = document.getElementById('box2');
      box.addEventListener('click',()=>{
        history.pushState('state',null,'?page=1');
      })
    
      window.addEventListener('popstate',function (ev) {
        console.log('popstate');
      })
      box2.addEventListener('click',()=>{
        history.back();
        setTimeout(()=>{
          console.log('timeout');
        })
      })
    script>
    

    MessageChannel的message回调

    
    <script>
    var btn = document.getElementById('btn');
    btn.onclick = function () {
      var channel = new MessageChannel();
        channel.port1.onmessage = function onmessage1 (){
          console.log('postMessage')
          Promise.resolve().then(function promise1 (){
              console.log('promise')
          })
        };
        setTimeout(function setTimeout2(){
          console.log('setTimeout')
        }, 0)
        channel.port2.postMessage(0);
    };
    script>
    

    I/O回调

    "file" id="input" multiple>
    <script>
      input.addEventListener('change', function () {
        var file = input.files[0]
        var reader = new FileReader()
        reader.onload = function (ev) {
          console.log(reader.result);
        }
        reader.readAsArrayBuffer(file)
      })
    script>
    

    2. 微任务有哪些?

    MutationObserver的回调、Promise的then catch finally回调、queueMicrotask.

    在合适时机,这些微任务会被推入微任务队列;每一次事件循环会从微任务队列中取所有任务并执行。

    Promise和queueMicrotask不支持IE, MutationObserver支持IE11;

    MutationObserver例子:

    下面的代码中box.textContent = 1的位置不同,代码的执行顺序就不同以验证MutationObserver为微任务。

    'box'>0
    <script> const box = document.getElementById('box'); const mo = new MutationObserver(function (mutations) { console.log('mutations') }) mo.observe(box, { childList: true }) box.onclick = function () { // box.textContent = 1; Promise.resolve().then(()=>{ console.log(333) }) box.textContent = 1; }; script>

    以下是使用`queueMicrotask`方法手动添加微任务的例子,可以不会对更高优先级的代码运行造成干扰。

    'box'>0
    <script> const box = document.getElementById('box'); box.onclick = function () { queueMicrotask(()=>{ console.log(121212) }) console.log(333) }; script>

    3. dom渲染是事件循环的一部分么?

    从规范的角度来看,DOM渲染是事件循环的一部分,可以将其视为一种渲染任务。

    如果宏任务或者微任务中发生了dom修改,因为一个渲染帧的时间可能远大于事件循环周期,所以不一定在本次事件循环会执行渲染任务。

    'box'>0
    <script> const box = document.getElementById('box'); box.onclick = function () { setTimeout(function setTimeout17 () { box.textContent = 1; }, 0) setTimeout(function setTimeout18 () { box.textContent = 2; }, 0) }; script>

    下图是上面的代码的执行流程,两个setTimeout的回调执行代表两次事件循环,在其后面出现了一个新的Task,仅执行了一次布局(layout)和绘制(paint);

    image

    4. requestAnimationFrame的回调是宏任务还是微任务?

    requestAnimationFrame的回调函数会在浏览器在下一帧渲染之前执行, 既不是宏任务,也不是微任务, 从规范上看是事件循环的一部分,从下图可以看到一个task下包含了requestAnimationFrame,layout paint, 可将其归类于渲染任务的一个可选步骤。

    'box'>0
    <script> const box = document.getElementById('box'); box.onclick = function () { setTimeout(function setTimeout17 () { box.textContent = 1; requestAnimationFrame(()=>{ console.log(111) Promise.resolve().then(()=>{ console.log(333) }) }) }, 0) setTimeout(function setTimeout18 () { box.textContent = 2; requestAnimationFrame(()=>{ console.log(222) }) }, 0) }; script>

    上述代码中添加了两个requestAnimationFrame,可以看到两者在一个Task内顺序执行;并且回调中的微任务也在这个Task内执行;

    image

    5. requestIdleCallback的回调是宏任务还是微任务?

    requestIdleCallback是事件循环的一部分,从图中可以看到,requestIdleCallback的回调是一个特殊的任务,这个函数的回调会在浏览器空闲时期被调用,所以不是每次循环都会执行该任务。

    
    <script>
    var btn = document.getElementById('btn');
    btn.onclick = function () {
      requestIdleCallback(function () {
        btn.innerHTML = "sdfsdfs"
        setTimeout(()=>{
          console.log(3)
        },0)
        Promise.resolve().then(()=>{
          console.log(4)
        })
      })
    };
    script>
    

    image

    该任务的优先级比较低,多个平行声明的requestIdleCallback会拆开成单一的task, 两个连续task之间甚至会被内部的setTimeout插足。

    for (let i = 0; i < 10; i++) {
      requestIdleCallback(() => {
        console.log('idle', Date.now() - a)
        setTimeout(()=>{
          console.log(12121)
        })
      })
    }
    

    image

    6. 事件循环图例

    image

  • 相关阅读:
    Spring Boot 链路追踪 SkyWalking 入门
    设计模式之状态模式
    北理工嵩天Python语言程序设计笔记(7 组合数据类型)
    No module named ‘haystack.urls‘等各种django报错解决方案
    计算机毕业设计springboot+vue+elementUI股票交易模拟系统
    汽车信息查询易语言代码
    使用beanUtil方法把Map赋值给实体类
    吴恩达机器学习课程笔记二
    解决:Package ‘setuptools‘ requires a different Python: 3.7.16 not in ‘>=3.8‘
    python循环判断
  • 原文地址:https://www.cnblogs.com/walkermag/p/17570671.html