JS是
单线程
的,JS是通过事件队列(Event Loop)
的方式来实现异步回调的。
一. 进程和线程
- 进程是操作系统资源分配的基本单位,进程中包含线程。(拥有资源和独立运行的最小单位)
- 线程由进程管理,是进程的执行单位。为了提升浏览器的稳定性和安全性,浏览器采用了多进程模型。
二. 浏览器5个进程
浏览器采用多进程模型,每一个标签页(Tab)就是一个独立的进程。
- 浏览器进程:负责界面显示、用户交互、子进程管理,提供存储等。
- 渲染进程:每个页面都有独立的渲染进程,主要负责页面的渲染工作。
- 网络进程:网络进程:主要负责加载网络资源(如 HTML、CSS、JavaScript 等)。
GPU
进程:负责 3D 绘制,提高图形处理性能。- 插件进程: 负责管理 Chrome 中安装的插件。
渲染进程:
- GUI渲染线程
- 渲染、布局和绘制页面
- 当页面需要重绘或回流时,GUI 渲染线程负责执行这些操作。
- 与JS引擎互斥
- JS引擎线程
- 负责解析执行JS脚本
- 只有一个JS引擎线程(单线程)
- 与GUI渲染线程互斥
- 事件触发线程
- 用来控制事件循环(鼠标点击、setTimeout、Ajax等)
- 当事件满足触发条件时,把时间放入JS引擎的执行队列中
- 定时器触发线程
- setInterval和setTimeout所在线程
- 计时完毕后会通知事件触发线程
- 异步HTTP请求线程
- 浏览器有一个单独的线程处理AJAX请求
- 当请求完毕后,如果有回调函数,会通知事件触发线程
三. eventloop使用
- 宏任务
- 页面的大部分任务是在主任务上执行的,如宏任务渲染事件、用户交互、JavaScript脚本执行、网路请求、文件读写等
- 宏任务会添加到消息队列的尾部,当主线程执行到该消息的时候就会执行
- 每次从事件队列中获取的就是一个宏任务,宏任务执行过程中不会执行其他其他内容
- 每次宏任务执行完毕后会进行GUI渲染线程的渲染,然后再执行下一个宏任务
- 宏任务颗粒度较大,不适合需要精确控制的任务
- 宏任务是有宿主方控制
- 微任务
- 宏任务结束后进行渲染然后执行下一个宏任务
- 微任务是当前宏任务执行后立即执行的任务
- 当宏任务执行完,就会先将执行期间所产生的所有微任务都执行完再去进行渲染
- 微任务由V8引擎控制,在厂家全局执行上下文的时候,也会在V8内部创建一个微任务队列
- 微任务: process.nextTick(Nodejs), Promises, Object.observe, MutationObserver
- 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 引擎再将结果返回给用户
libuv
- 同步执行全局的脚本
- 执行所有的微任务,先执行nextTick中的所有的任务,再执行其他微任务
- 开始执行宏任务,共有6个阶段,从第1个阶段开始,会执行每一个阶段所有的宏任务