当前位置: 首页 > news >正文

深度帖:浏览器的事件循环与JS异步

一、浏览器进程 

早期的浏览器是单进程的,所有功能杂糅在一个进程中;现在的浏览器是多进程的,包含浏览器进程、网络进程、渲染进程等等,每个进程负责的工作不同。

  • 浏览器进程:负责界面显示(地址栏、书签、历史记录)、窗口管理、标签页的创建和销毁、用户交互。
  • 网络进程:负责加载网络资源,HTTP请求等。
  • 渲染进程:负责执行HMTL、CSS、JS代码。每一个页面都会有一个或多个独立的渲染进程。

#浏览器对象模型(Browser Object Module, BOM)交互主要依赖浏览器进程。文档对象模型(Document Object Module ,DOM)主要依赖渲染进程。

还有一些其他的辅助进程,如GPU进程等。

在浏览器"自定义及控制"➡️"更多工具"➡️"任务管理器"查看浏览器进程情况:

二、渲染进程的渲染主线程和消息队列

渲染主线程

前端工程师编写的HTML格式文件代码由渲染进程负责解析最终绘制在页面上。

在这个过程中,渲染主线程是最繁忙的线程,其需要处理任务包括:

  • 解析HTML
  • 解析CSS
  • 计算样式
  • 布局
  • 处理图层
  • 每秒渲染页面60次(60帧)
  • 执行全局JS代码
  • 执行封装成任务的事件处理和计时器回调函数等

渲染主线程的任务多、任务频繁,其依赖消息队列(message queue,或事件队列)机制,先进先出原则调度任务。

任务源来自:1️⃣渲染主线程正在执行的任务产生的新任务,如JS代码运行产生的各种 2️⃣其他线程向消息队列递交的新任务,如网络请求、用户交互等。

消息队列的演变:宏/微任务队列➡️多任务队列

本章节关于事件循环中队列和队列优先级的内容。

浏览器的任务没有优先级,但消息队列有优先级。

传统将消息队列简单分为宏任务队列和微任务队列。

但浏览器逐渐复杂,在最新W3C标准下,浏览器不再有宏队列的说法,每个任务都有一个任务类型,在一次事件循环中,由浏览器自行决定哪一个队列的任务(浏览器真实的使用环境是复杂多变的)。但浏览器必须包含一个微队列,微队列的任务一定具有最高优先级,必须优先调度。

在目前Chrome的实现中,与前端开发最相关的队列,至少包含了以下几个:

  • 延时队列:用于存放计时器到时后的回调函数「中」。(setTimeOut、setInternel)
  • 交互队列:用于存放用户操作后产生的时间处理函数「高」。(addEventListener)
  • 微队列:用于存放需要最快执行的任务,优先级「最高」。(Promise.then)

面经总结

Q:为什么JavaScript是异步的?

A:从渲染主线程、同步的劣势和异步操作过程等角度回答。

  1. JS的运行环境JS是单线程的语言,这是因为它运行在浏览器渲染进程中的渲染主线程,渲染主线程只有一个。渲染主线程是浏览器线程里最繁忙的一个,承担了许多工作,解析HTML、解析CSS、计算样式、布局、处理图层、每秒渲染页面60次(60帧)、执行全局JS代码、封装成任务的事件处理和计时器回调函数(选几个回答)等都在其中执行。
  2. 如果使用同步的方式,极有可能会造成渲染主线程的阻塞,从而导致消息队列中的很多其他任务无法执行。这样一方面会导致主线程阻塞等待白白消耗时间,另一方面导致页面无法及时更新,给用户造成页面卡死现象。
  3. 所以浏览器采用异步的方式,具体做法是当某些任务发生时,比如计时器、网络请求、事件监听,主线程将任务分发交给其他线程去处理,自身立即结束该任务的执行,转而执行后续代码,当被转发任务的线程完成时,将事先传递的回调函数包装成任务加入到消息队列的末尾排队,等待主线程调度执行。在这种异步的模式下,浏览器用不阻塞,从而最大限度保证了单线程的流程运行。

Q:如何理解JavaScript中的事件循环?

A:事件循环也叫消息循环,是渲染主线程的工作方式。它帮助渲染主线程从不同优先级的队列中循环调度任务执行。传统将消息队列简单分为宏任务队列和微任务队列。但浏览器逐渐复杂,在最新W3C标准下,浏览器不再有宏队列的说法,每个任务都有一个任务类型,在一次事件循环中,由浏览器自行决定哪一个队列的任务(浏览器真实的使用环境是复杂多变的)。但浏览器必须包含一个微队列,微队列的任务一定具有最高优先级,必须优先调度。

练习题,阅读代码,写出控制台输出字母顺序:

1.

2. 

3. 

    答案:

    第一题:2 1

    第二题:3 2 1

    第三题:5 4 3 1 2

    http://www.lryc.cn/news/583334.html

    相关文章:

  • Foundry智能合约测试设计流程
  • 【25软考网工】第十章 (3)网络冗余设计、广域网接入技术
  • 【一起来学AI大模型】PyTorch DataLoader 实战指南
  • 前端交互自定义封装类:“双回调自定义信息弹窗”
  • ClickHouse 时间范围查询:精准筛选「本月数据」
  • pytorch 自动微分
  • Git 详解:从概念,常用命令,版本回退到工作流
  • sqlplus表结构查询
  • 3.常⽤控件
  • 跨平台ROS2视觉数据流:服务器运行IsaacSim+Foxglove本地可视化全攻略
  • 【动手学深度学习】4.9. 环境和分布偏移
  • MyBatis之数据操作增删改查基础全解
  • tinyxml2 开源库与 VS2010 结合使用
  • MySQL8.0基于GTID的组复制分布式集群的环境部署
  • 如何通过配置gitee实现Claude Code的版本管理
  • SpringBoot校园疫情防控系统源码
  • Flink1.20.1集成Paimon遇到的问题
  • stm32Cubmax的配置
  • 微信小程序91~100
  • Pycharm 报错 Environment location directory is not empty 如何解决
  • 基于Spring Boot+Vue的巴彦淖尔旅游网站(AI问答、腾讯地图API、WebSocket及时通讯、支付宝沙盒支付)
  • Ragas的Prompt Object
  • NHibernate案例
  • SAP ERP与Oracle EBS对比,两个ERP系统有什么区别?
  • aichat-core简化 LLM 与 MCP 集成的前端核心库(TypeScript)
  • C#项目 在Vue/React前端项目中 使用使用wkeWebBrowser引用并且内部使用iframe网页外链 页面部分白屏
  • Spring IoC 如何实现条件化装配 Bean?
  • HUAWEI HiCar6.0的新变化
  • 一条Redis命令是如何执行的?
  • C++随机打乱函数:简化源码与原理深度剖析