首页 > 其他分享 >浏览器事件循环

浏览器事件循环

时间:2024-02-05 17:32:57浏览次数:16  
标签:浏览器 渲染 队列 主线 任务 循环 事件 优先级

根据网络课程记录的一些笔记,受益匪浅

单线程是异步产生的原因

事件循环是异步的实现方式

浏览器运行会启动:

  • 浏览器进程
  • 网络进程
  • 渲染进程(一个标签页是一个渲染进程)

某进程崩溃后,互不影响

渲染进程

渲染进程启动后,会开启一个渲染主线程,主线程负责执行html,css,js代码

默认情况下,浏览器会为每一个标签页开启一个新的渲染进程,以保证标签页之间互不影响(未来可能会根据不同的操作系统,进行改进,以减少内存消耗,目前还是这样)

渲染主线程

渲染主线程工作很繁忙,任务包括但不限于:

  • 解析html
  • 解析css
  • 计算样式
  • 布局
  • 处理图层
  • 页面刷新渲染
  • 执行全局js代码
  • 执行事件处理函数
  • 执行计时器回调函数
  • ....

渲染主线程进入一个无限循环

事件循环

主线程从优先级最高的消息队列中拿到任务并执行完毕的过程就是一次事件循环

专业回答:

事件循环又叫消息循环,是浏览器渲染主线程的工作方式.

在chrome源码中,它开启一个无限循环,每次循环从消息队列中取出第一个任务执行,而其他线程只需要在适合时候将任务加入到队列末尾即可.

过去把消息队列简单的分为微队列和宏队列,此说法目前已无法满足复杂的浏览器环境,取而代之的是一种更灵活多变的处理方式.

根据W3C官方的解释,每个不同的任务都有对应的类型,同类型的任务必须在同一个队列,不同的任务可以分属于不同的队列,不同的队列有不同的优先级,在一次事件循环中,由浏览器自行决定取哪个队列的任务.但是浏览器必须有一个微队列,执行优先级最高,必须优先调度执行

异步

代码在执行过程中,会遇到一些无法立即处理的任务:计时器,网络通信,用户操作需执行的任务

这些耗时的操作,比如网络请求,计时器等如果一直在主线程中运行,会导致主线程阻塞(就是卡死),所以这些耗时的请求会放到专门对应的线程中,比如计时线程 网络线程中;当计时结束或网络请求完毕后,会将回调任务放到消息队列中,等待主线程的调度运行

JS是一门单线程语言,因为它运行在浏览器的渲染主线程中,而渲染主线程只有一个

主线程承担着诸多的工作,渲染页面,运行js都在这里

如果使用同步的方式,主线程极可能阻塞,从而导致消息队列中的其他任务无法执行

这样一方面导致主线程浪费时间,另一方面导致页面无法及时更新,用户看来就是卡死了

 

所以浏览器采用异步的方式来避免此种情况的发生,当主线程遇到计时器 网络 事件监听时,主线程会将任务交给对应的其他线程区处理,自己结束此任务再从消息队列中拿取其他任务执行,当其他线程完成后,会将回调函数包装成任务加入到消息队列末尾,等待主线程的调度执行

 

采用异步的方式,就能达到浏览器永不阻塞(任务的除外),从而最大限度的保证了单线程的流畅运行

js阻碍页面渲染

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JS阻碍渲染</title>
</head>
<body>
    <div id="test">你好,世界!</div>
    <button id="btn">更改上面</button>
    <script>
        // 死循环多少毫秒
        function shui(num=1000){
            let time1 = Date.now() + num
            while(Date.now()<time1){

            }
        }

        let div = document.getElementById("test")
        let btn = document.getElementById("btn")
        btn.addEventListener('click',function(){
            //修改文字
            div.innerText = "hello,world!"
            shui(3000)
        })
    </script>
</body>
</html>

上面的代码,结果是3秒后文字才改变;但是明明是先修改的文字内容后死循环的,为什么呢?

因为修改文字后,渲染主线程还没有进行绘制(绘制任务会放进消息队列),死循环就进来了,死循环阻塞的主线程,之后才调取了队列中的绘制任务并执行

任务的优先级

任务没有优先级,但是消息队列有优先级

  • 每个任务都有任务类型,同一个类型的任务必须在一个队列中,不同类型的任务可以分属于不同的队列.在一次事件循环中,浏览器可以根据实际情况从不同的队列中取出任务执行
  • 浏览器必须准备好一个微队列,微队列中的任务优先级最高

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

目前chrome中,至少有以下队列:

  • 延时队列,存放计时器到达后的回调任务;优先级[]
  • 交互队列,存放用户操作后产生的时间处理任务;优先级[]
  • 微队列,优先级[最高]

添加任务到微队列的主要方式:Promise 和 MutationObserver

例如

Promise.resolve().then(()=>{console.log("我是微队列任务")})

JS中的计时器能做到精确计时吗?

不能

  • js的计时器本质上是使用了操作系统的相关计时函数,而操作系统的计时函数本身就存在一定的偏差
  • 按照W3C的标准,浏览器的计时功能,如果嵌套超过5层,即从第6层开始就会带有4毫秒的最少计时时间,这样在计时低于4毫秒时就又带了一些偏差
  • 受事件循环的影响,计时器的回调任务只能在渲染主线程执行完优先级高于计时任务的其他任务后,才能调度执行计时任务,并不是到时后立即执行,所以又加上一些偏差

标签:浏览器,渲染,队列,主线,任务,循环,事件,优先级
From: https://blog.51cto.com/u_15668841/9611632

相关文章

  • Unity基于C#事件委托机制
    事件委托是一种用于实现观察者模式的设计模式,它允许对象在发生特定事件时通知其他对象。在Unity中,事件委托机制为开发者提供了一种简单而有效的方式来处理游戏中的事件和交互。一、事件委托的基本概念事件委托是一种特殊的类型,它可以持有一个或多个方法的引用。当某个事件发生时......
  • js 基于能力检测进行浏览器分析
    虽然可能有人觉得能力检测类似于黑科技,但恰当地使用能力检测可以精准地分析运行代码的浏览器。使用能力检测而非用户代理检测的优点在于,伪造用户代理字符串很简单,而伪造能够欺骗能力检测的浏览器特性却很难。检测特性可以按照能力将浏览器归类。如果你的应用程序需要使用特定的浏......
  • js 浏览器元数据
    navigator对象暴露出一些API,可以提供浏览器和操作系统的状态信息。GeolocationAPInavigator.geolocation属性暴露了GeolocationAPI,可以让浏览器脚本感知当前设备的地理位置。这个API只在安全执行环境(通过HTTPS获取的脚本)中可用。这个API可以查询宿主系统并尽可能精确......
  • js 浏览器分析
    想要知道自己代码运行在什么浏览器上,大部分开发者会分析window.navigator.userAgent返回的字符串值。所有浏览器都会提供这个值,如果相信这些返回值并基于给定的一组浏览器检测这个字符串,最终会得到关于浏览器和操作系统的比较精确的结果。相比于能力检测,用户代理检测还是有一定......
  • 问题:ca中,哪个测量事件会触发Scell配置?
    问题:ca中,哪个测量事件会触发Scell配置?A、A6B、A2C、A1D、A5参考答案如图所示......
  • 定制你的清爽Mac版Edge浏览器
    浏览器每次打开都有个烦人的提示要获取将来的microsoftedge更新,需要macos10.15或更高版本,找了很久也没有解决办法,有windows端的解决方案,有禁止更新的解决方案,就是没有Mac上如何避免这个告警的方案,于是走上Edge定制化之旅。省流直接下载下面的com.microsoft.Edge.......
  • 增强for循环和break continue
    增强for循环java5引入一种主要用于数组或者集合的增强型for循环java增强for循环语法格式如下:for(声明语句:表达式){//代码句子}声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。表达式:表达式......
  • "与事件处理程序不同,事件处理程序只在每次交互时运行一次,而 Effect 则在需要进行同步
    "与事件处理程序不同,事件处理程序只在每次交互时运行一次,而Effect则在需要进行同步时运行。"但是交互往往会同时触发事件处理,从而引起值变化,进而导致同步,从而运行Effect,不是吗?那么如何确定方法应该写在事件处理里还是Effect里面??事件处理程序(EventHandler)和React中的Effect(......
  • Github登录 2FA(Two-Factor Authentication/两因素认证) 浏览器插件-已验证
    Github登录2FA(Two-FactorAuthentication/两因素认证)浏览器插件-已验证chrome装下这个扩展身份验证器https://chromewebstore.google.com/detail/身份验证器/bhghoamapcdpbohphigoooaddinpkbai?pli=1装好以后,到登录的页面,有个二维码,用这个插件扫一下就能登录了,然后就有......
  • (12)动态生成菜单及绑定自定义事件
    varAddCollctMenus:ArrayOfTMenuItem;//动态菜单 procedureTForm1.Button5Click(Sender:TObject);Vari,AddCollctMenuCount:Integer;BeginAddCollctMenuCount:=Length(AddCollctMenus)-1;Fori:=0ToAddCollctMenuCountDoBeginFreeAndNil......