浏览器eventLoop

240 阅读5分钟

CPU、进程、线程之间的关系

  • 进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)
  • 线程是cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程)
  • 不同进程之间也可以通信,不过代价较大
  • 单线程多线程,都是指在一个进程内的单和多

浏览器是多进程的

对于计算机来说,每一个应用程序都是一个进程, 而每一个应用程序都会分别有很多的功能模块,这些功能模块实际上是通过子进程来实现的。 对于这种子进程的扩展方式,我们可以称这个应用程序是多进程的。

浏览器就是多进程的,主要有:

  • 主进程

    • 协调控制其他子进程(创建、销毁)
    • 浏览器界面显示,用户交互,前进、后退、收藏
    • 将渲染进程得到的内存中的Bitmap,绘制到用户界面上
    • 处理不可见操作,网络请求,文件访问等
  • 第三方插件进程

    • 每种类型的插件对应一个进程,仅当使用该插件时才创建
  • GPU进程

    • 用于3D绘制等
  • 渲染进程,就是我们说的浏览器内核

    • 负责页面渲染,脚本执行,事件处理等
    • 每个tab页一个渲染进程

那么浏览器中包含了这么多的进程,那么对于普通的前端操作来说,最重要的是什么呢?

答案是渲染进程,也就是我们常说的浏览器内核

渲染进程是多线程的

一个渲染进程包含:

  • 主线程

    • 调度其他
  • GUI渲染线程

    • 负责渲染页面,布局和绘制
    • 页面需要重绘和回流时,该线程就会执行
    • js引擎线程互斥,防止渲染结果不可预期
  • JS引擎线程

    • 负责处理解析和执行javascript脚本程序
    • 只有一个JS引擎线程(单线程)
    • GUI渲染线程互斥,防止渲染结果不可预期
  • 事件触发线程

    • 用来控制事件循环(鼠标点击、setTimeout、ajax等)
    • 当事件满足触发条件时,将事件放入到JS引擎所在的执行队列中
  • 定时触发器线程

    • setInterval与setTimeout所在的线程
    • 定时任务并不是由JS引擎计时的,是由定时触发线程来计时的
    • 计时完毕后,通知事件触发线程
  • 异步http请求线程

    • 浏览器有一个单独的线程用于处理AJAX请求
    • 当请求完成时,若有回调函数,通知事件触发线程
  • io线程

    • 用来接收其他进程的消息

为什么 GUI 渲染线程与 JS 引擎线程互斥? 这是由于 JS 是可以操作 DOM 的,如果同时修改元素属性并同时渲染界面(即 JS线程UI线程同时运行), 那么渲染线程前后获得的元素就可能不一致了。 因此,为了防止渲染出现不可预期的结果,浏览器设定 GUI渲染线程JS引擎线程为互斥关系, 当JS引擎线程执行时GUI渲染线程会被挂起,GUI更新则会被保存在一个队列中等待JS引擎线程空闲时立即被执行。

每个渲染进程都有一个主线程,并且主线程非常繁忙,既要处理 DOM,又要计算样式,还要处理布局,同时还需要处理 JavaScript 任务以及各种输入事件。要让这么多不同类型的任务在主线程中有条不紊地执行,这就需要一个系统来统筹调度这些任务,这个统筹调度系统就是消息队列和事件循环

eventLoop

image.png

  1. 很多事情都是在主线程做的,例如:js代码执行,页面布局计算,渲染等
  2. 主线程同一时刻只能做一件事,事情多了就要排队。所以主线程维护了任务队列。
  3. 某个事件发生时,事件触发线程 就把对应的任务添加到主线程的任务队列中。
  4. 主线程上的任务完成之后,就会从任务队列中取出任务来执行。

事件发生时,将其回调添加到任务队列;主线程上的任务完成之后,就会从任务队列中取出任务来执行,此过程不断重复从而形成一个循环,称为eventLoop。

setTimeout

定时触发器线程会在指定时间之后,将其回调添加到主线程的任务队列

微任务

通常我们把消息队列中的任务称为宏任务,每个宏任务中都包含了一个微任务队列,在执行宏任务的过程中,如果有优先级更高的任务,添加到微任务列表中,这样就不会影响到宏任务的继续执行,也就解决了执行效率的问题。

等宏任务中的主要功能都直接完成之后,这时候,渲染引擎并不着急去执行下一个宏任务,而是执行当前宏任务中的微任务,这样也就解决了实时性问题。

总之,它能控制任务执行的优先级。