• 事件循环深度学习


    事件循环

    学习事件循环 首先就要了解一下浏览器的进程模型

    浏览器的进程模型

    何为进程?
    • 如果要运行一个程序 就要属于他自己的内存空间 可以把这块内存空间简单理解为进程
    • 什么对象,函数啥啥啥的 都是放在内存里面的 所以就需要开辟一块内存空间让这个程序自己去使用
    • 总结 : 进程 == 开辟出的这块内存空间

    每一个应用 至少也要有一个进程 进程之间相互独立 即使要通讯 也需要双方同意才行
    比如qq进程崩溃了 肯定是不会影响到微信的 所以每一个进程是完全独立的

    何为线程?
    • 有了进程后 就可以运行程序的代码了
    • 运行代码的称之为线程

    一个进程至少有一个线程 所以在进程开启后会自动创建一个线程来运行代码 该线程称之为主线程

    如果程序需要同时执行多块代码 主线程就会启动更多的 线程来执行代码 所以一个进程中可以包含多个线程

    浏览器有哪些进程和线程

    浏览器是一个多进程多线程的应用程序
    • 浏览器内部工作极其复杂
    • 为了避免相互影响 为了减少连环崩溃的几率 当浏览器启动后 它会开启多个进程

    可以在浏览器的任务管理器中查看当前的所有进程

    其中最主要的进程有
    • 浏览器进程 : 主要负责页面的显示 用户交互 子进程管理等 浏览器进程内部会启动多个线程处理不同的任务
    • 网络进程 : 负责加载网络资源 网络进程内部会启动多个线程来处理不同的网络任务
    • 渲染进程(重点) : 渲染进程启动后 会开启一个渲染主线程 , 主线程负责执行HTML,CSS,JS代码

    默认情况下浏览器会为为每个标签页开启一个新的渲染进程 以保证不同的标签页之间不相互影响

    渲染主线程是如何工作的?

    ps: 事件循环就在主线程中执行

    • 渲染主线程是浏览器中最繁忙的线程 需要他处理的任务非常多
    • 解析html,css,js,计算样式,布局,处理图层,每秒把页面画60次.....

    要处理这么多的任务 主线程如何去调度指挥任务呢?

    比如:

    • 我正在执行一个js函数,执行到一半的时候用户点击了按钮, 我该立即去执行点击事件的处理函数吗?
    • 我正在执行一个js函数,执行到一半额时候某个计时器到达了时间,我改立即去实现它的回调函数吗?
    • 浏览器进程通知我用户点击了按钮 ,与此同时,某个计时器也到达了时间,我应该处理哪一个呢?

    渲染主线程指挥调度核心的方法 排队

    以下的整个过程,被称之为事件循环 也可以说消息循环

    • 在最开始的时候 渲染主线程会进入一个无限的循环
    • 每一次循环会检查事件队列中是否有任务存在,如果有,就取出第一个任务执行,执行完后进入下一次循环,如果没有就休息
    • 其他所有线程 (包括其他进行的线程比如网络线程,监听用户交互的线程等等..) ,可以随时向事件队列里添加任务,新任务会加到事件队列的末尾,在添加新任务的时候,如果主线程是休眠状态,则会唤醒以继续循环拿去任务

    事件循环的细节

    异步是什么?

    在代码执行过程中, 会遇到一些无法立即处理的任务

    • 计时完成后需要执行的任务—setTimeOut,setInterval
    • 网络通信后需要执行的任务—XHR,Fetch
    • 用户操作后需要执行的任务—addEventListener

    执行这些任务 都是需要未知的时间的 ,不能立即去执行

    如果让渲染主线程等待这类型任务的时机达到,就会导致主线程长期处于阻塞的状态 从而导致浏览器卡死!!!

      setTimeOut(()=>{
    
        console.log(1);
    
      },300)
      
      console.log(2);
      // 如果先等待3秒后打印1  然后再打印2  这就是同步
      // 显然 这样是不可行的 因为一个定时器所有的任务都被阻塞了 
      // 那渲染主线程怎么执行绘制页面任务?怎么执行事件交互任务?  直接废了!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    渲染主线程承担着极其重要的工作,无论如何都不能阻塞!!!

    因此浏览器选择用异步来解决这个问题

    • 比如主线程在事件队列中遇到一个定时器
    • 主线程会派一个计时线程去开始执行定时 主线程是不会自己计时的, 他要干别的活,此时,在主线程眼里,定时器这个的任务已经执行了
    • 主线程然后继续去执行后面的任务
    • 突然定时器时间到了!, 定时器会把需要执行的回调函数继续放在事件队列里的末端 当成一个普通的任务去执行
    • 此时,主线程也不会知道 这个任务就是当时定时器里的回调函数 依旧进行循环的执行任务
    这样使用异步的方式: 渲染主线程永不阻塞!!!

    之前遇到过一个面试题 , 如何理解js的异步?

    学到这里后 , 我现在会选择这样去回答

    • js是一个单线程的语言 , 这是因为js只能运行在浏览器的渲染主线程中,而渲染主线程只有一个 , 而渲染主线程承担着非常的多的事情 , 渲染页面 , 执行js啥啥的 都在里面执行
    • 如果使用同步的方式 , 就极可能导致主线程阻塞 , 从而导致消息队列中的很多其他任务无法得到执行 , 这样一来 , 繁忙的主线程白白浪费时间等待 , 导致页面也会无法更新 , 给用户造成卡死的状态
    • 所以浏览器选择用异步方式来避免 , 具体做法就是当某些任务发生时 , 比如计时器 , 网络 , 事件监听 , 主线程将任务会交给其他线程去处理 , 自身立刻结束任务的执行 , 转而执行后面的任务 , 当其他线程完成时 , 把那个回调函数包装成任务 , 加入到事件队列的末端排队 , 等待主线程调度
    • 这样的一个模式 就是异步 可以保证浏览器不阻塞

    js为何阻塞渲染?

    • 因为js和渲染都处于一个线程上面
    • 假如点击按钮修改文字 点击完按钮后 文字已经修改了 但是还没绘制在页面上 要等渲染任务执行后才会绘制

    任务有优先级吗?

    任务没有优先级

    但是消息队列 有优先级

    根据w3c的最新解释

    • 每个任务都有一个任务类型 , 同一个类型的任务必须在一个队列也就是一共有多个队列 , 不同类型的任务可以分属不同的队列,在一个次事件循环中,浏览器可以根据实际情况从不同的队列中区出任务执行
    • 浏览器必须准备好一个微队列 , 微队列中的任务优先所有其他任务执行他里面的东西 所有都要给我等 连绘制任务 都要等 就是最高优先级了

    随着浏览器的复杂度急剧提升 W3C不再使用宏队列的说法

    在目前chrome的实现中 至少包含了下面的队列

    • 延时队列 : 用于存放计时器到达后的回调任务 , 优先级中
    • 交互列队 : 用于存放用户操作后产生的事件处理任务 , 优先级高
    • 微队列 : 用户存放需要最快执行的任务 优先级最高

    添加任务到微队列的主要方式是使用promise,....
    列如

    // 立刻把一个函数添加到微队列 最高执行
      promise.resolve().then(函数)
    
      setTimeOut(()=>{ // 第三步执行延时队列中的任务
        console.log(1);
      },0)
    
      promise.resolve().then(()=>{ // 第二步执行微队列中的任务
        console.log(2);
      })
    
      console.log(3); // 第一步先执行全局js
    
      // 3 2 1
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    面试题 : 阐述一下js的事件循环

    • 事件循环又叫消息循环官方叫event loop 浏览器实现叫message loop , 是浏览器选择主线程的工作方式 , 在chrome中就是开启了一个不会结束的for循环, 每次循环从消息队列中取出第一个任务执行,而其他线程只需要在合适的时候将任务加入到队列末尾即可
    • 过去吧消息队列简单分为宏队列和微队列,这种说法目前无法满足复杂的浏览器环境,取而代之的是一种更加灵活多变的处理方式
    • 根据w3c官方的解释, 每个任务有不同的类型,同类型的任务必须在同一个队列,不同的任务可以属于不同的队列.不同的任务队列有不同的优先级,在一次事件循环中,由浏览器自行决定取那个队列的任务,但浏览器必须有一个微队列,微队列的任务一定具有最高的优先级,必须有限调度执行—
      title: 事件循环深度学习
      tags: [js]
      date: 2022-09-31 16:09:29
      categories: [js]

    事件循环

    学习事件循环 首先就要了解一下浏览器的进程模型

    浏览器的进程模型

    何为进程?
    • 如果要运行一个程序 就要属于他自己的内存空间 可以把这块内存空间简单理解为进程
    • 什么对象,函数啥啥啥的 都是放在内存里面的 所以就需要开辟一块内存空间让这个程序自己去使用
    • 总结 : 进程 == 开辟出的这块内存空间

    每一个应用 至少也要有一个进程 进程之间相互独立 即使要通讯 也需要双方同意才行
    比如qq进程崩溃了 肯定是不会影响到微信的 所以每一个进程是完全独立的

    何为线程?
    • 有了进程后 就可以运行程序的代码了
    • 运行代码的称之为线程

    一个进程至少有一个线程 所以在进程开启后会自动创建一个线程来运行代码 该线程称之为主线程

    如果程序需要同时执行多块代码 主线程就会启动更多的 线程来执行代码 所以一个进程中可以包含多个线程

    浏览器有哪些进程和线程

    浏览器是一个多进程多线程的应用程序
    • 浏览器内部工作极其复杂
    • 为了避免相互影响 为了减少连环崩溃的几率 当浏览器启动后 它会开启多个进程

    可以在浏览器的任务管理器中查看当前的所有进程

    其中最主要的进程有
    • 浏览器进程 : 主要负责页面的显示 用户交互 子进程管理等 浏览器进程内部会启动多个线程处理不同的任务
    • 网络进程 : 负责加载网络资源 网络进程内部会启动多个线程来处理不同的网络任务
    • 渲染进程(重点) : 渲染进程启动后 会开启一个渲染主线程 , 主线程负责执行HTML,CSS,JS代码

    默认情况下浏览器会为为每个标签页开启一个新的渲染进程 以保证不同的标签页之间不相互影响

    渲染主线程是如何工作的?

    ps: 事件循环就在主线程中执行

    • 渲染主线程是浏览器中最繁忙的线程 需要他处理的任务非常多
    • 解析html,css,js,计算样式,布局,处理图层,每秒把页面画60次.....

    要处理这么多的任务 主线程如何去调度指挥任务呢?

    比如:

    • 我正在执行一个js函数,执行到一半的时候用户点击了按钮, 我该立即去执行点击事件的处理函数吗?
    • 我正在执行一个js函数,执行到一半额时候某个计时器到达了时间,我改立即去实现它的回调函数吗?
    • 浏览器进程通知我用户点击了按钮 ,与此同时,某个计时器也到达了时间,我应该处理哪一个呢?

    渲染主线程指挥调度核心的方法 排队

    以下的整个过程,被称之为事件循环 也可以说消息循环

    • 在最开始的时候 渲染主线程会进入一个无限的循环
    • 每一次循环会检查事件队列中是否有任务存在,如果有,就取出第一个任务执行,执行完后进入下一次循环,如果没有就休息
    • 其他所有线程 (包括其他进行的线程比如网络线程,监听用户交互的线程等等..) ,可以随时向事件队列里添加任务,新任务会加到事件队列的末尾,在添加新任务的时候,如果主线程是休眠状态,则会唤醒以继续循环拿去任务

    事件循环的细节

    异步是什么?

    在代码执行过程中, 会遇到一些无法立即处理的任务

    • 计时完成后需要执行的任务—setTimeOut,setInterval
    • 网络通信后需要执行的任务—XHR,Fetch
    • 用户操作后需要执行的任务—addEventListener

    执行这些任务 都是需要未知的时间的 ,不能立即去执行

    如果让渲染主线程等待这类型任务的时机达到,就会导致主线程长期处于阻塞的状态 从而导致浏览器卡死!!!

      setTimeOut(()=>{
    
        console.log(1);
    
      },300)
      
      console.log(2);
      // 如果先等待3秒后打印1  然后再打印2  这就是同步
      // 显然 这样是不可行的 因为一个定时器所有的任务都被阻塞了 
      // 那渲染主线程怎么执行绘制页面任务?怎么执行事件交互任务?  直接废了!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    渲染主线程承担着极其重要的工作,无论如何都不能阻塞!!!

    因此浏览器选择用异步来解决这个问题

    • 比如主线程在事件队列中遇到一个定时器
    • 主线程会派一个计时线程去开始执行定时 主线程是不会自己计时的, 他要干别的活,此时,在主线程眼里,定时器这个的任务已经执行了
    • 主线程然后继续去执行后面的任务
    • 突然定时器时间到了!, 定时器会把需要执行的回调函数继续放在事件队列里的末端 当成一个普通的任务去执行
    • 此时,主线程也不会知道 这个任务就是当时定时器里的回调函数 依旧进行循环的执行任务
    这样使用异步的方式: 渲染主线程永不阻塞!!!

    之前遇到过一个面试题 , 如何理解js的异步?

    学到这里后 , 我现在会选择这样去回答

    • js是一个单线程的语言 , 这是因为js只能运行在浏览器的渲染主线程中,而渲染主线程只有一个 , 而渲染主线程承担着非常的多的事情 , 渲染页面 , 执行js啥啥的 都在里面执行
    • 如果使用同步的方式 , 就极可能导致主线程阻塞 , 从而导致消息队列中的很多其他任务无法得到执行 , 这样一来 , 繁忙的主线程白白浪费时间等待 , 导致页面也会无法更新 , 给用户造成卡死的状态
    • 所以浏览器选择用异步方式来避免 , 具体做法就是当某些任务发生时 , 比如计时器 , 网络 , 事件监听 , 主线程将任务会交给其他线程去处理 , 自身立刻结束任务的执行 , 转而执行后面的任务 , 当其他线程完成时 , 把那个回调函数包装成任务 , 加入到事件队列的末端排队 , 等待主线程调度
    • 这样的一个模式 就是异步 可以保证浏览器不阻塞

    js为何阻塞渲染?

    • 因为js和渲染都处于一个线程上面
    • 假如点击按钮修改文字 点击完按钮后 文字已经修改了 但是还没绘制在页面上 要等渲染任务执行后才会绘制

    任务有优先级吗?

    任务没有优先级

    但是消息队列 有优先级

    根据w3c的最新解释

    • 每个任务都有一个任务类型 , 同一个类型的任务必须在一个队列也就是一共有多个队列 , 不同类型的任务可以分属不同的队列,在一个次事件循环中,浏览器可以根据实际情况从不同的队列中区出任务执行
    • 浏览器必须准备好一个微队列 , 微队列中的任务优先所有其他任务执行他里面的东西 所有都要给我等 连绘制任务 都要等 就是最高优先级了

    随着浏览器的复杂度急剧提升 W3C不再使用宏队列的说法

    在目前chrome的实现中 至少包含了下面的队列

    • 延时队列 : 用于存放计时器到达后的回调任务 , 优先级中
    • 交互列队 : 用于存放用户操作后产生的事件处理任务 , 优先级高
    • 微队列 : 用户存放需要最快执行的任务 优先级最高

    添加任务到微队列的主要方式是使用promise,....
    列如

    // 立刻把一个函数添加到微队列 最高执行
      promise.resolve().then(函数)
    
      setTimeOut(()=>{ // 第三步执行延时队列中的任务
        console.log(1);
      },0)
    
      promise.resolve().then(()=>{ // 第二步执行微队列中的任务
        console.log(2);
      })
    
      console.log(3); // 第一步先执行全局js
    
      // 3 2 1
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    面试题 : 阐述一下js的事件循环

    • 事件循环又叫消息循环官方叫event loop 浏览器实现叫message loop , 是浏览器选择主线程的工作方式 , 在chrome中就是开启了一个不会结束的for循环, 每次循环从消息队列中取出第一个任务执行,而其他线程只需要在合适的时候将任务加入到队列末尾即可
    • 过去吧消息队列简单分为宏队列和微队列,这种说法目前无法满足复杂的浏览器环境,取而代之的是一种更加灵活多变的处理方式
    • 根据w3c官方的解释, 每个任务有不同的类型,同类型的任务必须在同一个队列,不同的任务可以属于不同的队列.不同的任务队列有不同的优先级,在一次事件循环中,由浏览器自行决定取那个队列的任务,但浏览器必须有一个微队列,微队列的任务一定具有最高的优先级,必须有限调度执行
  • 相关阅读:
    LeetCode //C - 50. Pow(x, n)
    张成方案——Span Programs
    《SpringBoot项目实战》第五篇—接口发生异常如何统一处理
    实例010:给人看的时间
    C++的作用域和命名空间
    C#提高编程效率专辑--自动代码生成器
    一步一步用numpy实现神经网络各种层
    celery apply_async定时任务重复执行问题
    nginx做负载均衡服务器,配置动静分离
    11. 第一章总结
  • 原文地址:https://blog.csdn.net/haosicx/article/details/126851098