Skip to content

JS是单线程的,JS是通过事件队列(Event Loop)的方式来实现异步回调的。

一. 进程和线程

  • 进程是操作系统资源分配的基本单位,进程中包含线程。(拥有资源和独立运行的最小单位)
  • 线程由进程管理,是进程的执行单位。为了提升浏览器的稳定性和安全性,浏览器采用了多进程模型。

二. 浏览器5个进程

浏览器采用多进程模型,每一个标签页(Tab)就是一个独立的进程。

  • 浏览器进程:负责界面显示、用户交互、子进程管理,提供存储等。
  • 渲染进程:每个页面都有独立的渲染进程,主要负责页面的渲染工作。
  • 网络进程:网络进程:主要负责加载网络资源(如 HTML、CSS、JavaScript 等)。
  • GPU进程:负责 3D 绘制,提高图形处理性能。
  • 插件进程: 负责管理 Chrome 中安装的插件。

渲染进程:

  1. GUI渲染线程
    • 渲染、布局和绘制页面
    • 当页面需要重绘或回流时,GUI 渲染线程负责执行这些操作。
    • 与JS引擎互斥
  2. JS引擎线程
    • 负责解析执行JS脚本
    • 只有一个JS引擎线程(单线程)
    • 与GUI渲染线程互斥
  3. 事件触发线程
    • 用来控制事件循环(鼠标点击、setTimeout、Ajax等)
    • 当事件满足触发条件时,把时间放入JS引擎的执行队列中
  4. 定时器触发线程
    • setInterval和setTimeout所在线程
    • 计时完毕后会通知事件触发线程
  5. 异步HTTP请求线程
    • 浏览器有一个单独的线程处理AJAX请求
    • 当请求完毕后,如果有回调函数,会通知事件触发线程

三. eventloop使用

EventLoop

EventLoop2

  1. 宏任务
    • 页面的大部分任务是在主任务上执行的,如宏任务渲染事件、用户交互、JavaScript脚本执行、网路请求、文件读写等
    • 宏任务会添加到消息队列的尾部,当主线程执行到该消息的时候就会执行
    • 每次从事件队列中获取的就是一个宏任务,宏任务执行过程中不会执行其他其他内容
    • 每次宏任务执行完毕后会进行GUI渲染线程的渲染,然后再执行下一个宏任务
    • 宏任务颗粒度较大,不适合需要精确控制的任务
    • 宏任务是有宿主方控制
  2. 微任务
    • 宏任务结束后进行渲染然后执行下一个宏任务
    • 微任务是当前宏任务执行后立即执行的任务
    • 当宏任务执行完,就会先将执行期间所产生的所有微任务都执行完再去进行渲染
    • 微任务由V8引擎控制,在厂家全局执行上下文的时候,也会在V8内部创建一个微任务队列
    • 微任务: process.nextTick(Nodejs), Promises, Object.observe, MutationObserver
  3. eventloop实现
    • JS 分为同步任务和异步任务
    • 同步任务都在JS引擎线程上执行,形成一个执行栈
    • 事件触发线程管理一个任务队列,异步任务触发条件达成,将回调事件放到任务队列中
    • 执行栈中所有同步任务执行完毕,此时JS引擎线程空闲,系统会读取任务队列,将可运行的异步任务回调事件添加到执行栈中,开始执行
    • setTimeout/setInterval JS引擎线程=>定时触发器线程=>事件触发线程=>事件队列
    • Ajax JS引擎线程=>异步http请求线程=>事件触发线程=>事件队列

Node中的EventLoop

  • Node.js 采用 V8 作为 JavaScript 解析引擎,I/O 操作使用自研的 libuv 库。
  • libuv是一个基于事件驱动的跨平台抽象层,封装了不同操作系统一些底层特性,对外提供统一的API
  • 事件循环也在它里面实现
    • libuv 是一个基于事件驱动的跨平台抽象层,封装了不同操作系统的底层特性,并对外提供统一的 API。
    • libuv库负责Node Api的执行,它将不同的任务分配给不同的线程,通过 Event Loop 以异步方式将任务的执行结果返回给 V8。
    • V8 引擎再将结果返回给用户

img

libuv

  • 同步执行全局的脚本
  • 执行所有的微任务,先执行nextTick中的所有的任务,再执行其他微任务
  • 开始执行宏任务,共有6个阶段,从第1个阶段开始,会执行每一个阶段所有的宏任务

img

image-20240408231110988