Web Worker:JS多线程的伪解药💊?

前言

在前端开发领域,JavaScript 的单线程限制一直是一个难以忽视的挑战。当谈到解决JavaScript的单线程限制时,HTML5引入的Web Worker被普遍认为是一剂解药💊。同时,业界中大量的文章也是聚焦于讨论web worker的神奇力量。然而,本文将另辟蹊径,和您一同探索Web Worker神秘的另一面。

js单线程限制及多线程能力的需求

js的一大特点就是单线程。对于单线程,一句最经典的概述就是"同一时间只能执行一个任务"。

在JavaScript设计之初,其单线程模型是由其主要用途和初衷所决定的。JavaScript作为一门浏览器端的脚本语言,最初的主要任务是处理用户交互。设想一下,如果JavaScript被设计为多线程,那么在用户交互中进行多线程同步操作DOM,为了保证准确性必然会引发竞态分配等复杂问题。另外,对于客户端来说也可能引发性能和资源消耗等多种复杂问题,这时的多线程在这个背景下反而显得过于臃肿。

随着Web技术的不断演进,JavaScript的单线程模型已经无法满足日益复杂的业务需求和以及带来的性能要求。为了解决这些问题,JavaScript引入了一些如ajax、setTimeout、requestAnimationFrame等机制,但需要明确的是,这些机制不过是通过EventLoop来制造的多线程假象,其并没有改变JavaScript单线程的本质。

然而,随着现代Web应用中用户交互变得越来越复杂,以及数据处理需求的增加,JavaScript应用需要更强的计算能力和更快的响应性,单线程模型面临着着挑战愈发严重。这就引出了一个关键问题:如何在保持JavaScript的单线程特性的同时,充分利用多核处理器来提高性能以满足用户的需求?在这样的背景下,Web Worker技术应运而生。

web worker的定义

或许在您的项目中尚未使用过Web Worker,但实际上它已经存在了很长时间。早在2009年,W3C便提出了 Web Worker 草案,2011年正式称为HTML5标准的一部分。

W3C对Web Worker的定义如下:

an API for running scripts in the background independently of any user interface scripts.

兼容性
一个最最最最最简易明了的示例
js 复制代码
// 主线程代码
const worker = new Worker('worker.js');
worker.postMessage('Hello from the main thread!');
worker.onmessage = (event) => {
  console.log('Message from Web Worker:', event.data);
};

// worker.js
self.onmessage = (event) => {
  console.log('Message from main thread:', event.data);
  self.postMessage('Hello from the Web Worker!');
};
其他类型扩展

除了专用worker类型(Dedicated Web Worker),还有两种特殊的 Web Worker:SharedWorer和ServiceWorker。

  • SharedWorker可以在多个浏览器Tab中访问到同一个Worker实例, 可实现多Tab共享数据, 如共享webSocket连接等。然而看起来很美好, 但兼容性问题一度严重。特别是safari从12年ios5开始支持,到在ios7~15中一度不支持,再到ios16又支持,真是一波三折纠结死了!
  • ServiceWorker可以拦截和处理网络请求,实现离线缓存、推送通知和其他高级网络功能,也非常有意思。

sharedWorker兼容性:

Web Worker 的优势

Web Worker与主线程相互独立,可以在不影响主线程性能的情况下执行一些耗时操作而不会阻塞主线程,特别是在处理复杂任务和长时间运行的操作时非常有用。它的主要优势如下:

  • 独立运行:Web Worker在一个独立的线程中运行,与主线程互不干扰。这可以避免主线程因执行耗时操作而卡顿,从而提高页面的响应速度和用户体验。
  • 隔离机制:Web Worker无法直接访问或操作DOM元素。这种隔离机制可以防止潜在的页面修改和恶意攻击,确保网页的安全性和稳定性。
  • 多线程异步执行: Web Worker允许您在标签页进程中创建多个独立线程,每个线程都可以在不同的内核上执行,因此可以实现并行计算。

Web Worker的实际应用场景

在实际项目中,Web Worker更适合处理以下场景:

  • 计算密集型任务:例如,数据分析、图像处理、加密算法等。这些任务通常耗时较长且对 CPU性能要求较高。通过将这些任务委托给Web Worker,可以避免阻塞主线程,从而保持应用的响应性。
  • 异步操作:当需要执行一些异步操作时,例如从服务器获取数据,可以使用Web Worker来避免因为等待异步操作完成而导致的主线程卡顿。
  • 人工智能:Web Worker可以用于处理机器学习模型的训练或推断时的并行计算,从而提高模型的训练速度和响应性。

Web Worker的局限性

  • 兼容性问题:尽管HTML5已经普及,但Web Worker在大多数厂商的浏览器中仅在最新的大版本中支持,在旧版本中支持并不好。这导致开发者在实际项目中可能会放弃使用Web Worker。

  • 资源限制:无法直接访问DOM、同源策略、无法读取本地文件等等。

  • 编程复杂性:使用Web Worker需要处理线程间的通信,这增加了编程的复杂性。对于简单的任务使用Web Worker可能并不划算。

  • 安全性问题:因Web Worker是在后台运行的多线程,所以更隐蔽,由此也带来了新的安全问题:如恶意脚本的注入以利用Web Worker大规模执行多线程攻击来放大攻击效果、恶意创建大量web worker并让它们执行高负载的任务而导致浏览器崩溃、importScripts没有跨域限制可能会加载不受信任的脚本从而导致安全漏洞等等。

Web Worker的通信效率问题

除了以上局限性,Web Worker的通信效率问题也是值得注意的一点。

  • 无法共享内存: 与传统多线程编程不同,Web Worker不能直接共享内存,主页面与worker之间的数据传递的是通过拷贝而不是共享来完成的。因此在如大文件传输场景下可能会消耗大量内存和处理时间。

  • 数据序列化问题: 当主线程与Web Worker之间在数据交换时需要对数据进行序列化和反序列化,序列化会阻塞发送方,而反序列化会阻塞接收方。因此即使是小型数据,也需要经过这一过程。这可能在频繁通信的情况下积累成为性能瓶颈。

  • postMessage:postMessage方法本身并不是导致通信效率低下的主要原因,而是由于如Worker线程需要频繁地向主线程发送大量消息,或者消息体积较大等其他因素造成。这可能会导致主线程处理消息的速度跟不上Worker线程发送消息的速度,从而引起通信拥塞和性能问题。

sequenceDiagram participant MainThread as 主线程 participant WorkerThread as Web Worker MainThread->>WorkerThread: postMessage(data) WorkerThread->>MainThread: onmessage(event) MainThread-->>MainThread: 序列化数据 MainThread->>WorkerThread: 发送序列化数据 Note right of MainThread: 数据传输 WorkerThread-->>WorkerThread: 反序列化数据 WorkerThread->>MainThread: postMessage(replyData) MainThread-->>MainThread: 序列化回复数据 MainThread->>WorkerThread: 发送序列化回复数据 Note right of MainThread: 数据传输 WorkerThread-->>WorkerThread: 反序列化回复数据
通信效率带来的问题

由于通信效率的限制,Web Worker不适用于实时任务。由于消息传递的开销和可能的网络延迟,Web Worker无法保证实时性。因此,对于在线游戏、视频会议等一些需要实时响应场景,使用Web Worker可能不是最佳选择。

思考:有了 Web Worker API,JavaScript 是否真的变成了一门多线程语言?

在 MDN 的web worker的"线程安全"一节中提到:

"The Worker interface spawns real OS-level threads"

从底层技术角度来看,Web Worker确实具备多线程的特性。然而这与传统多线程编程语言(如Java或C++)有所不同。如,JavaScript中的主线程与Web Worker之间采用的是消息传递来通信,而不是直接共享内存。这意味着Web Worker之间无法直接共享数据,而传统多线程语言则可以直接共享内存,从而实现更直接和高效的线程间通信。

尽管Web Worker从底层技术上看具备多线程的特性,但如今JavaScript的主要应用领域仍然是处理用户界面和用户交互,Web Worker的引入主要是为了改善前端应用程序的响应性,使其能够更好地处理一些计算密集型任务,而不是将JavaScript彻底转变为多线程编程语言。

总结而言,尽管Web Worker API为JavaScript提供了一定程度的多线程支持,但JavaScript仍然是一门主要依赖于单线程执行的编程语言,Web Worker并没有改变JavaScript的单线程本质。

js多线程技术的未来展望

未来的js多线程应该具有更好的性能、更好的响应性以及更多的并行计算能力,随着Web技术不断发展,多线程前景越发广阔。如:

  • 浏览器引擎及硬件加速技术的不断发展,使得在浏览器中处理复杂的多线程任务更为可行;
  • WebAssembly等新技术的融合可能带来的机遇;
  • 随着ECMAScript标准的不断发展,可以期待更多多线程相关的功能和API的引入,以更好地支持并行计算。

总结

综上所述,Web Worker作为一种在前端开发中多线程的工具,的确有其独特的优势。然而,我们必须明白,尽管它被广泛视为解决JavaScript单线程问题的一种方式,但它并没有从根本上改变JavaScript的单线程本质。在使用Web Worker时,我们仍然需要面对资源限制、通信开销以及安全性等问题。因此,可以说Web Worker是一种"伪解药"。它有助于缓解JavaScript单线程的限制,但并非银弹。在未来,我们期待JavaScript多线程领域的进一步发展,以实现Web能力的持续提升🚀🚀🚀!

参考文献

developer.mozilla.org/en-US/docs/...

dassur.ma/things/is-p...

扩展问题

以Chrome为例,同一tab进程下的多 Web Worker 线程,能否在多物理内核上运行?

【完】🐥

相关推荐
前端小小王16 分钟前
React Hooks
前端·javascript·react.js
迷途小码农零零发25 分钟前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀1 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪1 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
ekskef_sef3 小时前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine6413 小时前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻4 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云4 小时前
npm淘宝镜像
前端·npm·node.js
dz88i84 小时前
修改npm镜像源
前端·npm·node.js
Jiaberrr4 小时前
解锁 GitBook 的奥秘:从入门到精通之旅
前端·gitbook