「这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战」。
前言
前端开发者常说:“JavaScript 是单线程的。”这种说法虽然有些简单,但描述了 JavaScript 在浏览器 中的一般行为。因此,作为帮助 Web 开发人员理解 JavaScript 的教学工具,它非常有用。 单线程就意味着不能像多线程语言那样把工作委托给独立的线程或进程去做。JavaScript 的单线程 可以保证它与不同浏览器 API 兼容。假如 JavaScript 可以多线程执行并发更改,那么像 DOM 这样的 API 就会出现问题。因此,POSIX 线程或 Java 的 Thread 类等传统并发结构都不适合 JavaScript。 而这也正是工作者线程的价值所在:允许把主线程的工作转嫁给独立的实体,而不会改变现有的单 线程模型
工作者线程简介
JavaScript 环境实际上是运行在托管操作系统中的虚拟环境。在浏览器中每打开一个页面,就会分 配一个它自己的环境。这样,每个页面都有自己的内存、事件循环、DOM,等等。每个页面就相当于 一个沙盒,不会干扰其他页面。对于浏览器来说,同时管理多个环境是非常简单的,因为所有这些环境 都是并行执行的。 使用工作者线程,浏览器可以在原始页面环境之外再分配一个完全独立的二级子环境。这个子环境 不能与依赖单线程交互的 API(如 DOM)互操作,但可以与父环境并行执行代码。
工作者线程与线程
通常需要将工作者线程与执行线程进行比较。在许多方面,这是一个恰当的比较,因为 工作者线程和线程确实有很多共同之处。
- 工作者线程是以实际线程实现的。例如,Blink 浏览器引擎实现工作者线程的 WorkerThread 就 对应着底层的线程。
- 工作者线程并行执行。虽然页面和工作者线程都是单线程 JavaScript 环境,每个环境中的指令则 可以并行执行。
工作者线程可以共享某些内存。工作者线程能够使用 SharedArrayBuffer 在多个环境间共享 内容。虽然线程会使用锁实现并发控制,但 JavaScript 使用 Atomics 接口实现并发控制。 工作者线程与线程有很多类似之处,但也有重要的区别。
- 工作者线程不共享全部内存。在传统线程模型中,多线程有能力读写共享内存空间。除了 SharedArrayBuffer 外,从工作者线程进出的数据需要复制或转移。
- 工作者线程不一定在同一个进程里。通常,一个进程可以在内部产生多个线程。根据浏览器引 擎的实现,工作者线程可能与页面属于同一进程,也可能不属于。例如,Chrome 的 Blink 引擎对 共享工作者线程和服务工作者线程使用独立的进程。
- 创建工作者线程的开销更大。工作者线程有自己独立的事件循环、全局对象、事件处理程序和 其他 JavaScript 环境必需的特性。创建这些结构的代价不容忽视
无论形式还是功能,工作者线程都不是用于替代线程的。HTML Web 工作者线程规范是这样说的:
工作者线程相对比较重,不建议大量使用。例如,对一张 400 万像素的图片,为每个像素 都启动一个工作者线程是不合适的。通常,工作者线程应该是长期运行的,启动成本比较高, 每个实例占用的内存也比较大。
工作者线程的类型
Web 工作者线程规范中定义了三种主要的工作者线程:专用工作者线程、共享工作者线程和服务工 作者线程。现代浏览器都支持这些工作者线程。
- 1.专用工作者线程 专用工作者线程,通常简称为工作者线程、Web Worker 或 Worker,是一种实用的工具,可以让脚 本单独创建一个 JavaScript 线程,以执行委托的任务。专用工作者线程,顾名思义,只能被创建它的页 面使用。
- 2.共享工作者线程 共享工作者线程与专用工作者线程非常相似。主要区别是共享工作者线程可以被多个不同的上下文 使用,包括不同的页面。任何与创建共享工作者线程的脚本同源的脚本,都可以向共享工作者线程发送 消息或从中接收消息。
- 3.服务工作者线程 服务工作者线程与专用工作者线程和共享工作者线程截然不同。它的主要用途是拦截、重定向和修 改页面发出的请求,充当网络请求的仲裁者的角色。
WorkerGlobalScope
在网页上,window 对象可以向运行在其中的脚本暴露各种全局变量。在工作者线程内部,没有 window 的概念。这里的全局对象是 WorkerGlobalScope 的实例,通过 self 关键字暴露出来。
WorkerGlobalScope 属性和方法
navigator:返回与工作者线程关联的 WorkerNavigator。
- self:返回 WorkerGlobalScope 对象。
- location:返回与工作者线程关联的 WorkerLocation。
- performance:返回(只包含特定属性和方法的)Performance 对象。
- console:返回与工作者线程关联的 Console 对象;对 API 没有限制。
- caches:返回与工作者线程关联的 CacheStorage 对象;对 API 没有限制。
- indexedDB:返回 IDBFactory 对象。
- isSecureContext:返回布尔值,表示工作者线程上下文是否安全。
- origin:返回 WorkerGlobalScope 的源。 类似地,self 对象上暴露的一些方法也是 window 上方法的子集。这些 self 上的方法也与 window 上对应的方法操作一样。
- atob()
- btoa()
- clearInterval()
- clearTimeout()
- createImageBitmap()
- fetch()
- setInterval()
- setTimeout() WorkerGlobalScope 还增加了新的全局方法 importScripts(),只在工作者线程内可用
WorkerGlobalScope 的子类
实际上并不是所有地方都实现了 WorkerGlobalScope。每种类型的工作者线程都使用了自己特定 的全局对象,这继承自 WorkerGlobalScope。
- 专用工作者线程使用 DedicatedWorkerGlobalScope。
- 共享工作者线程使用 SharedWorkerGlobalScope。
- 服务工作者线程使用 ServiceWorkerGlobalScope。