zoukankan      html  css  js  c++  java
  • Event Loop

    Event Loop

    定义:

    event - 事件 loop - 循环,既然叫事件循环,那么循环的点在哪?
    
    循环的是一个又一个的任务队列,这些任务队列由宏任务和微任务构成
    

    两条原则

    1. 一次处理一个任务
    2. 一个任务开始后直到完成,不会被其他任务中断

    事件处理之间的关系

    一次事件处理中,最多处理一个宏任务,但是会处理所有的微任务,任务开始后,会将内部所有的宏观函数加到宏观队列等待,会将所有的围观函数加到微观队列等待,当前宏任务处理完毕后,开始逐个处理微任务,当微任务执行处理完成,会检查是否需要更新ui,如果是则重新渲染ui。之后再次检查宏观任务队列中的下一个宏观任务,取出来并且执行

    执行规则

    image

    宏任务包含:

    1. 整体script(也叫js主进程)
    2. settimeout
    3. setinterval
    4. requestAnimationFrame

    微任务包含:

    1. Promise.then catch finally
    2. Generator 函数
    3. async/await
    4. MutationObserver

    异步任务都有哪些

    1. 回调函数
    2. Promise(注意new promise里面的属于同步任务)
    3. async
    4. Generator
    5. 事件监听
    6. 发布/订阅
    7. 计时器
    8. requestAnimationFrame
    9. MutationObserver

    题外话

    浏览器的渲染,不同浏览器处理是不同的,但是大部分浏览器选择一般是一秒钟60帧率(也就是60fps),这意味着浏览器会在16ms左右渲染一帧,所以我们单个任务和该任务的所有附属微任务都应该在16ms内完成,以达到显示平滑流畅。

    怎么证明js事件队列存在,就拿简单的setTimeout来说

    console.time("settimeout");
    setTimeout(() => {
      console.log('settimeout执行')
      console.timeEnd("settimeout");
    }, 1000);
    for (let a = 0; a < 50000; a++) {
      console.log(a)
    }
    

    time会输出多少呢?这种事件是怎么触发的?

    代码是在一秒钟后才加入事件队列,之后等待执行。怎么证明,很简单我们在拿出一个定时器

    console.time("settimeout");
    console.time("settimeout2");
    setTimeout(() => {
      console.log('settimeout执行')
      console.timeEnd("settimeout");
    }, 1000);
    setTimeout(() => {
      console.log('settimeout2执行')
      console.timeEnd("settimeout2");
    }, 1);
    for (let a = 0; a < 50000; a++) {
      console.log(a)
    }
    

    明明后声明的定时器2,却先执行了,不知道有没有发现,定时器1和定时器2执行时间,很接近!!!这很重要,为什么会这样,这就是今天所说的事件队列,当1毫秒时候,定时器2被加入了事件队列,当一秒钟时候定时器1被加入事件队列,然后for执行完后,队列中下一个任务为定时器2,但是他只有一个console故而很快,所以拿出了定时器进行执行。再来看个额外例子,来巩固下:

    console.time("settimeout");
    console.time("settimeout2");
    setTimeout(() => {
      console.log('settimeout执行')
      console.timeEnd("settimeout");
    }, 1000);
    
    for (let a = 0; a < 50000; a++) {
      console.log(a)
    }
    
    setTimeout(() => {
      console.log('settimeout2执行')
      console.timeEnd("settimeout2");
    }, 1);
    

    枯燥无味的定义基本就这样,我们来从实践来做分析

    从练习题来说

    console.log(1)
    setTimeout(function () {
      console.log(2)
      setTimeout(function () {
        console.log(3)
      })
    })
    setTimeout(function () {
    console.log(4)
    })
    console.log(5)
    

    第一次运行之后
    image.png

    这时当前主进程队列已经结束,开始检测微任务队列是否还有未完成的任务,发现微任务队列已经空了所以,当前宏任务队列结束,开始下一组宏任务

    image.png

    settimeout2任务完结,检查当前微任务队列为空,开始下一组宏任务

    image.png

    所以最终答案为:
    1,5,2,4,3

    • 注意:setTimeout是以其触发事件为写入队列时间。如果这么说不理解的话 可以将上面代码改为如下:
    console.log(1)
    setTimeout(function () {
      console.log(2)
      setTimeout(function () {
        console.log(3)
      })
    })
    setTimeout(function () {
      console.log(4)
    })
    console.log(5)
    

    这样就会输出1,5,2,3,4

    tips: setTimeout写的1000不等于他就是在上一次事件结束后的1000ms,而是以他声明开始就进行计时的。不过这不是本篇文章的核心,我们不深究他的逻辑,继续看第二个例子

    console.log(1)
    setTimeout(() => {
      console.log('2')
      Promise.resolve(4).then((res) => console.log(res))
    }, 0);
    setTimeout(() => {
      console.log('3')
    }, 0);
    

    image.png

    主进程执行完毕,检查微任务队列为空,当前宏任务结束,开启下一组宏任务

    image.png

    settimeout宏任务执行完毕,检查宏任务队列,拿出settimeout3的宏任务,将它拿出来执行。这个比较简单咱们就不画图了

    所以本题答案为1,2,4,3

    let promise = new Promise(function(resolve, reject) {
      console.log('1');
      resolve();
    });
    
    promise.then(function() {
      console.log('2');
    });
    console.log(3)
    

    image.png

    检查宏任务队列发现为空,所以本次代码结束

    答案为:1,3,2

    console.log(1);
    setTimeout(function(){
      console.log(2);
    }, 0);
    Promise.resolve().then(function(){
      console.log(3);
    }).then(function(){
      console.log(4);
    });
    

    image.png

    当前宏任务已经结束,查看宏任务队列中发现还有settimeout没有执行,将它取出来执行,
    输出2.

    所以本题答案为:1,3,4,2

    setTimeout(()=>{
      console.log('1');
    },0);
    var obj={
      func:function () {
          setTimeout(function () {
              console.log('2')
          },0);
          return new Promise(function (resolve) {
              console.log('3');
              resolve();
          })
      }
    };
    obj.func().then(function () {
      console.log('4')
    });
    console.log('5');
    

    image.png

    主线进程及其微观进程执行完毕,会拿出下一组settimeout1执行,执行后会进行检测微观队列,如果没有则会继续往下取出settimeout2执行。至此程序结束。

    所以本题答案为:3,5,4,1,2

    console.log(1)
    const p = new Promise(function(resolve, reject) {
      console.log(2)
      resolve()
    }).then(() => {
      console.log(3)
      throw(new Error('错误'))
    }).catch(() => {
      console.log(4)
    })
    console.log(5)
    setTimeout(() => {
      console.log(6)
    }, 0);
    console.log(7)
    p.then(() => {
      console.log(8)
      throw(new Error('错误2'))
    })
    
    

    image.png

    至此,当前宏任务结束。检查宏任务队列。取出下一组宏任务,settimeout6,并执行

    所以答案为:1,2,5,7,3,4,8,Error,6

    最后留两道题给大家做学习用。

    如果不能一眼看出。可以像我一样画一个图。进行梳理。本文中为了代码整齐,settimeout都是直接简化为尽快执行。其实settimeout应该是在到达他声明的时间时候,才进入宏观队列排队的。

    根据以下规则,你将不会在遇到事件队列的问题

    1. 一次处理一个任务
    2. 宏任务+当前所有微任务为一组
    3. 同步直接执行、异步会加入事件队列
    4. 所有异步的加入队列时间均以他们触发时间为准
    console.log(1)
    const p = new Promise(function(resolve, reject) {
      console.log(2)
      setTimeout(() => {
        console.log(9)
      }, 0);
      resolve()
    }).then(() => {
      console.log(3)
      throw(new Error('错误'))
    }).catch(() => {
      console.log(4)
    })
    console.log(5)
    setTimeout(() => {
      console.log(6)
    }, 0);
    console.log(7)
    p.then(() => {
      console.log(8)
      throw(new Error('错误2'))
    })
    
    console.log(1)
    const p = new Promise(function(resolve, reject) {
      console.log(2)
      setTimeout(() => {
        console.log(9)
      }, 0);
      resolve()
    }).then(() => {
      console.log(3)
      throw(new Error('错误'))
    }).catch(() => {
      console.log(4)
    })
    console.log(5)
    setTimeout(() => {
      console.log(6)
    }, 0);
    console.log(7)
    p.then(() => {
      console.log(8)
      throw(new Error('错误2'))
    })
    requestAnimationFrame(function() {
      console.log(10)
    })
    

    宏观微观

    宏观微观不是嵌套!!!!!即使代码嵌套了在队列中也不是嵌套的!!!!

    怎么利用这些,在代码中优化自己的代码,举个例子来说

    for (let i = 0 ; i<50000;i++) {
      const div = document.createElement('div')
      div.innerText = i
      document.body.appendChild(div)
    }
    
    
    
    function slice(startSplitNumber, total, sliceNumber, cb) {
      const oneNumber = total/sliceNumber
      const start = startSplitNumber * oneNumber
      const end = (startSplitNumber + 1) * oneNumber
      if (start >= total) return
      setTimeout(() => {
        for(let i = start; i < end;i++) {
          cb(i)
        }
        slice(startSplitNumber + 1, total, sliceNumber, cb)
      }, 0)
    }
    
    slice(0, 50000, 5000, function (current) {
      const div = document.createElement('div')
      div.innerText = current
      document.body.appendChild(div)
    })
    
  • 相关阅读:
    jeecg t:treeSelectTag 联动处理
    saas动态数据源
    jquery ajax超时设置
    创建mysql 数据库脚本
    Java动态创建MySQL数据库
    ant执行sql脚本
    jeecg jeewx 多表查询展示
    @JoinColumn 详解
    hibernate关联映射注解及@JoinColumn的用法
    算发帖——俄罗斯方块覆盖问题一共有多少个解
  • 原文地址:https://www.cnblogs.com/jinzhenzong/p/14075557.html
Copyright © 2011-2022 走看看