Skip to content

事件循环

🕒 Published at:

浏览器的进程模型

进程与线程

进程是正在运行的程序的实例,每个进程都有自己独立的一块内存空间,有了进程之后,才可以运行程序的代码。一个进程至少有一个线程,叫做主线程,一个进程也可以运行多个线程,多个线程可以共享数据。

浏览器是一个多进程多线程的应用程序

浏览器是一个极其复杂的应用程序

为了避免多个功能之间的相互影响,减少崩溃的几率,浏览器在启动的时候会启动多个进程。

其中,最主要的进程有

  1. 浏览器进程

    负责界面显示、用户交互、子进程管理等。

  2. 网络进程

    负责网络通信,各种资源的加载。

  3. 渲染进程

    每个标签页都是一个新的渲染进程,渲染进程的主线程负责执行 HTML、CSS、JS 代码

渲染主线程是如何工作的

渲染主线程是浏览器中最繁忙的线程,它处理的任务包括:

  • 解析 html

  • 解析 css

  • 计算样式

  • 布局

  • 处理图层

  • 每秒把页面画 60 次

  • 执行全局 js 代码

  • 处理事件函数

  • 执行计时器的回调

  • ......

为了处理这么多任务的调度,主线程想出了一个办法:队列

当渲染主线程正在执行任务时,由渲染主线程和浏览器其他线程安排的任务会被暂时放到队列中等待执行,当渲染主线程中的任务执行完毕,会从队列中依次取出排队的任务进行执行

  1. 在最开始的时候,渲染主线程会进入一个无线循环

    cpp
    for(;;){
    
    }
  2. 每一次循环会检查队列中是否有任务存在,如果有,就取出第一个任务进行执行,执行完后进入下一次循环;如果没有,则进入休眠状态。

  3. 其他所有的线程(包括其他进程的线程)可以随时向队列添加任务,新添加的任务会被放到队列的末尾。添加任务时,如果主线程是休眠状态,则唤醒

何为异步?

在代码执行的过程中,无法立即处理(需要等待)的任务就是异步任务,比如:

  • 计时器安排的任务 setTimeoutsetInterval

  • 网络通信后执行的任务 XHRfetch

  • 用户操作后执行的任务 addEventListner

渲染主线程不会等待以上任务的执行,而是继续执行主线程需要执行的代码。当以上任务(由浏览器的其他进程)执行完成时,会把执行后的结果放到队列中进行等待(直到主线程清空后依次从队列中取出)。

JS 代码为何会阻塞渲染

因为 js 代码和页面渲染都会在渲染主线程中进行执行,而页面的重绘是异步的过程,需要等待主线程的 js 代码执行完毕。

队列中的任务有优先级吗?

队列中的任务没有优先级,都是先进先出

但是队列有优先级

  • 每个任务都有一个任务类型,同一个类型的任务必须在同一个队列,不同类型的任务可以分属不同的队列。在一次事件循环中,浏览器可以根据实际情况从不同队列中取出任务执行

  • 浏览器必须准备好一个微队列,微队列中的任务优先于其他所有任务执行(btw:微队列,vip 的 v)

    添加任务到微队列的方式 PromsieMutationObserver

    javascript
    // 立刻把一个函数放到微队列
    Promise.resolve().then(函数)

最后更新于: