🎁🎁🎁详解 Web Worker,不再止步于会用!

本文涉及的产品
数据传输服务 DTS,数据同步 small 3个月
推荐场景:
数据库上云
数据传输服务 DTS,数据迁移 small 3个月
推荐场景:
MySQL数据库上云
数据传输服务 DTS,数据同步 1个月
简介: 前面的章节都是告诉你怎么使用Worker,并没有真正的深入Worker的原理,这一章我们就来详细的了解一下Worker的原理。

前面的章节都是告诉你怎么使用Worker,并没有真正的深入Worker的原理,这一章我们就来详细的了解一下Worker的原理。

Worker 的全局作用域

WorkerGlobalScopeWorker的全局作用域,它继承自EventTarget

EventTarget

EventTarget是一个接口,它定义了一些方法,用来注册和触发事件,它的实现有WindowWorkerGlobalScopeNodeXMLHttpRequest等。

它拥有三个方法:

  • addEventListener:注册事件
  • removeEventListener:移除事件
  • dispatchEvent:触发事件

这些方法应该都很熟悉,我们在平时的开发中也经常使用,例如:

function onMessage (event) {
   
   
  console.log(event.data)
}

window.addEventListener('message', onMessage);

window.dispatchEvent(new MessageEvent('message', {
   
    data: 'hello' }));

window.removeEventListener('message', onMessage);

上面就是一个简单的事件注册、触发、移除的例子,这些都是通过EventTarget来实现的。

WorkerGlobalScope

上面提到了WorkerGlobalScopeWorker的全局作用域,它继承自EventTarget,所以它也拥有addEventListenerremoveEventListenerdispatchEvent这三个方法。

它还有一些其他的属性和方法:

标准属性和方法

标准属性指的是规范中已经确定的属性,通常情况下它们都是固定不会再发生太大的变化:

  • self(只读):指向当前的WorkerGlobalScope,它的值和this是一样的;
  • location(只读):指向当前WorkerGlobalScopeURL,它是一个Location对象;
  • navigator(只读):指向当前WorkerGlobalScopeNavigator对象;
  • importScripts():用来加载脚本,它的参数是一个或多个脚本的URL,例如:importScripts('a.js', 'b.js'),它的返回值是undefined,如果加载失败,会抛出一个NetworkError的异常;
  • onerror:用来注册error事件的回调函数;
  • onlanguagechange:用来注册languagechange事件的回调函数;
  • onoffline:用来注册offline事件的回调函数;
  • ononline:用来注册online事件的回调函数;
  • onrejectionhandled:用来注册rejectionhandled事件的回调函数,它是Promise的一个事件;
  • onunhandledrejection:用来注册unhandledrejection事件的回调函数, 它是Promise的一个事件;

在之前的示例中,我们用过selfimportScriptsonerror,这些都是WorkerGlobalScope的标准属性。

参考:the-workerglobalscope-common-interface

非标准属性和方法

非标准属性指的是规范中还没有确定的属性,它们的实现是不稳定的,可能会在未来的版本中发生变化,所以不建议在生产环境中使用。

  • performance:指向当前WorkerGlobalScopePerformance对象,它是一个常规的Performance对象;
  • console:指向当前WorkerGlobalScopeConsole对象,它是一个常规的Console对象;
  • dump():用来打印日志,几乎没有浏览器实现了这个方法,所以不建议使用,直接使用console.log就可以了;

上面这些属性和方法这里就先简单的了解一下,因为每一个属性都够一个单独的文章来讲解,所以这里就暂时不展开了。

self

self是一个只读属性,它指向当前的WorkerGlobalScope,它的值和this是一样的。

上面所有提到的属性都可以使用self来访问,例如:self.locationself.navigatorself.console等等。

同时也可以省略self,直接使用属性名来访问,例如:locationnavigatorconsole等等。

这就像window对象一样,它的属性和方法都可以使用window来访问,也可以省略window,直接使用属性名来访问,例如:

// 通过 window 访问 location 属性
window.location.href

// 直接使用 location 属性
location.href

// 也可以使用 this 访问 location 属性
this.location.href

WorkerGlobalScope中,selfthis是一样的,所以可以省略self,直接使用属性名来访问,例如:

// 通过 self 访问 location 属性
self.location.href

// 直接使用 location 属性
location.href

// 也可以使用 this 访问 location 属性
this.location.href

其他属性的访问方式也是一样的,例如:navigatorconsole等等。

所以我们在写worker.js的时候,可以省略self,直接使用属性名来访问,例如:

addEventListener('message', function (event) {
   
   
  console.log(event.data)
})

上面的console其实也是一个WorkerGlobalScope的属性,它指向当前WorkerGlobalScopeConsole对象。

这些都和我们使用window对象是一样的,所以我们可以把WorkerGlobalScope当成一个window对象来使用。

可以使用什么

上面着重的讲解了一下self属性,再加上最开始介绍了一下WorkerGlobalScope的属性和方法,所以我们可以把WorkerGlobalScope当成一个window对象来使用。

但是WorkerGlobalScopewindow对象还是有一些区别的,我们来看一下下面这个例子:

// worker.js
addEventListener('message', function (e) {
   
   
  console.log(e.data)
  console.log(location.href)
})

省略main.js的代码,很简单,这里只探索worker.js的代码。

虽然这里也可以使用location属性,但是它的指向并不是window.location,而是WorkerLocation.location

它们之间的区别也很明显,window.location指向的是当前页面的地址,而WorkerLocation.location指向的是worker.js文件的地址。

不过这个并不是我们这次要谈论的主要问题,只是告诉大家,虽然在WorkerGlobalScope能用到很多和window对象一样的属性和方法,但是它们的结果,或者说指向的对象是不一样的。

Worker中可以使用Web API有很多,但是它并不是挂载子啊WorkerGlobalScope,而是通过类似混入的方式,进入到Worker的上下文中,有如下:

  • Broadcast Channel API
  • Cache API
  • Channel Messaging API
  • Console API
  • Crypto
  • CustomEvent
  • Data Store(仅 Firefox)
  • DOMRequest 和 DOMCursor
  • Fetch
  • FileReader
  • FileReaderSync(仅在 worker 中可用)
  • FormData
  • ImageData
  • IndexedDB
  • Network Information API
  • Notifications
  • Performance
  • PerformanceEntry
  • PerformanceMeasure (en-US)
  • PerformanceMark (en-US)
  • PerformanceObserver
  • PerformanceResourceTiming
  • Promise
  • Server-sent 事件
  • ServiceWorkerRegistration
  • TextEncoder 和 TextDecoder
  • URL
  • WebGL 中的 OffscreenCanvas(通过特性首选项 gfx.offscreencanvas.enabled 启用)
  • WebSocket
  • XMLHttpRequest(尽管 responseXML 和 channel 属性始终为 null)

上面的列表来自MDN Worker 中可用的 Web API

除了上面提到的这些Web API,还有一些WorkerGlobalScope的属性和方法,就是文章最开始提到的,其他的就都不能使用了,例如documentwindowparent等等。

DedicatedWorkerGlobalScope

上面讲到WorkerGlobalScope其实也就是一个接口,它是DedicatedWorkerGlobalScope的父接口;

DedicatedWorkerGlobalScope就是Web Worker的父类,这个就是我们今天的主角。

属性和方法

直接看函数签名:

interface DedicatedWorkerGlobalScope extends WorkerGlobalScope {
   
   
    readonly name: DOMString;

    postMessage(message: any, transfer?: Transferable[]): void;
    postMessage(message: any, options?: Transferable): void;

    close(): void;

    onmessage: EventHandler;
    onmessageerror: EventHandler;
}

在这里就出现了我们最开始使用的postMessageonmessageonmessageerror,这三个属性和方法是DedicatedWorkerGlobalScope的属性和方法。

看到函数签名之后,我们发现postMessage方法有两种重载,第二个参数是可选的,目前并没有找到它的具体用法;

通过HTML Standard的描述,这个参数是用来传输数据的,具体怎么用还没找到相关的资料,这个暂时先不讨论了。

Worker就是实现了这个接口,所以我们可以在Worker中使用这些属性和方法。

Worker是我之前讲的三个Web Worker最简单的一个,看这个函数签名也就明白了,并没有太多的东西。

数据传输

Workerwindow之间的数据传输,是通过postMessageonmessage来实现的。

在我最开始的文章就讲过,postMessage只能传递JSON格式的数据,而且传递的数据是clone的,所以在Worker中修改传递过来的数据,不会影响到window中的数据。

但是这个clone的过程是有一定的限制的,并不是单纯的使用JSON.stringifyJSON.parse来实现的,而是使用结构化克隆算法来实现的,可以参考structuredClone

上面这个API是浏览器的一个新特性,并不是Web Worker的特性。

JSON.stringify来举例,在转换的数据中如果包含functionundefinedsymbol等类型的数据,会忽略这些数据类型的数据,将符合转换的数据转换成JSON格式的。

但是在Worker中,这些类型的数据是不能被clone的,所以会直接报错,错误信息会通过onmessageerror来传递。

const worker = new Worker('worker.js');
const obj = {
   
   
  a: 1,
  b: undefined,
  c: function () {
   
   },
  d: Symbol('d'),
};
console.log(JSON.stringify(obj)); // {"a":1}
worker.postMessage(obj); // Uncaught DOMException: Failed to execute 'postMessage' on 'Worker'

上面的示例中,使用JSON.stringify来转换数据,可以看到bcd这三个属性都没有被转换,但是在Worker中,这三个属性都会报错。

const worker = new Worker('worker.js');
var obj1 = {
   
   
    name: 'obj1'
}

var obj2 = {
   
   
    obj1: obj1,
    name: 'obj2'
}
obj1.obj2 = obj2;
worker.postMessage(obj2);
console.log(JSON.stringify(obj2));

image.png

循环依赖使用JSON.stringify会报错,但是在Worker中,这种情况是可以被clone的。

了解这些我们就可以更自如的使用Worker了,你可以把Worker还有主线程都当做一个独立的服务器,它们之间的数据传输,就是通过postMessageonmessage来实现的。

然后传递过去的数据,就是通过结构化克隆算法来实现的,只能传递JSON格式的数据,数据接收方可以随意操作这些数据,不会影响到数据发送方。

是不是很像服务器发送数据到前端,然后前端随便操作这些数据,不会影响到服务器。

总结

通过WorkerGlobalScope这个对象,我们可以看到Workerwindow之间的区别,Worker中没有DOMBOMwindowdocument等对象,但是它们都有navigatorlocationXMLHttpRequest等对象。

同时也可以使用很多Web API,比如setTimeoutsetIntervalfetchpostMessage等。

再通过WorkerGlobalScope认识到了DedicatedWorkerGlobalScope,它是Worker的一个实例,它的原型链上有WorkerGlobalScope,所以Worker中可以使用WorkerGlobalScope中的所有属性和方法。

然后通过WorkerGlobalScopeonmessagepostMessage,我们知道了Worker和主线程之间的数据传输,它们之间的数据传输,就是通过postMessageonmessage来实现的。

最后onmessagepostMessage的数据传输,是通过结构化克隆算法来实现的,只能传递JSON格式的数据,数据接收方可以随意操作这些数据,不会影响到数据发送方。

虽然说Worker是一个独立的线程,但是通过这上面一系列的操作,Web Worker就没有线程安全的问题,就不像其他多线程语言一样,需要加锁来保证线程安全。

历史章节和预告

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
Sqoop 企业级大数据迁移方案实战
Sqoop是一个用于在Hadoop和关系数据库服务器之间传输数据的工具。它用于从关系数据库(如MySQL,Oracle)导入数据到Hadoop HDFS,并从Hadoop文件系统导出到关系数据库。 本课程主要讲解了Sqoop的设计思想及原理、部署安装及配置、详细具体的使用方法技巧与实操案例、企业级任务管理等。结合日常工作实践,培养解决实际问题的能力。本课程由黑马程序员提供。
目录
相关文章
vite环境引入web worker方法
在 vite 环境中使用 web worker 时,如果遇到生产环境中 worker.js 文件的 MIME 类型被识别为 text/html,导致报错无法运行的情况时,可以参考以下两种方法,原理都是避免编译时产出单独的 worker.js 文件。方法一worker文件不需要包装,引入时后缀增加 ?worker&inline,使用时直接 new ImportedWorker();self.
1008 1
|
4月前
|
缓存 JavaScript 前端开发
JavaScript进阶 - Web Workers与Service Worker
【7月更文挑战第4天】JavaScript的Web Workers和Service Worker增强了Web性能。Web Workers处理后台多线程,减轻主线程负担,但通信有开销,受同源策略限制。Service Worker则用于离线缓存和推送通知,需管理其生命周期、更新策略,并确保安全。两者都带来了挑战,但也极大提升了用户体验。通过理解和优化,开发者能构建更高效、安全的Web应用。
116 2
|
4月前
|
缓存 前端开发 JavaScript
JavaScript进阶 - Web Workers与Service Worker
【7月更文挑战第10天】在Web开发中,Web Workers和Service Worker提升性能。Workers运行后台任务,防止界面冻结。Web Workers处理计算密集型任务,Service Worker则缓存资源实现离线支持。常见问题包括通信故障、资源限制、注册错误及缓存更新。通过示例代码展示了两者用法,并强调生命周期管理和错误处理的重要性。善用这些技术,可构建高性能的Web应用。
77 0
|
4月前
|
缓存 JavaScript 前端开发
JavaScript进阶 - Web Workers与Service Worker
【7月更文挑战第6天】JavaScript的Web Workers和Service Worker增强了浏览器的性能处理和离线功能。Web Workers处理后台计算,减轻主线程压力,但通信有开销,受同源策略限制。Service Worker则能拦截网络请求,支持离线缓存和推送通知,但其生命周期和权限管理需谨慎处理。通过理解它们的工作原理和限制,开发者能创建更流畅、更健壮的Web应用。
91 0
|
6月前
|
移动开发 JavaScript 前端开发
Web Worker:JavaScript的后台任务解决方案
Web Worker:JavaScript的后台任务解决方案
|
6月前
|
移动开发 JavaScript 前端开发
【JavaScript技术专栏】Web Worker在JavaScript中的应用
【4月更文挑战第30天】HTML5的Web Worker API解决了JavaScript单线程性能瓶颈问题,允许在后台线程运行JS代码。本文介绍了Web Worker的基本概念、类型、用法和应用场景,如复杂计算、图像处理和数据同步。通过实例展示了搜索建议、游戏开发和实时数据分析等应用,并提醒注意其无法直接访问DOM、需消息传递通信以及移动端资源管理。Web Worker为前端开发提供了多线程能力,提升了Web应用性能和用户体验。
74 0
|
6月前
|
JavaScript 安全 数据处理
Web Worker:让网页飞起来的幕后英雄(下)
Web Worker:让网页飞起来的幕后英雄(下)
Web Worker:让网页飞起来的幕后英雄(下)
|
6月前
|
缓存 编解码 数据处理
Web Worker:让网页飞起来的幕后英雄(上)
Web Worker:让网页飞起来的幕后英雄(上)
Web Worker:让网页飞起来的幕后英雄(上)
|
Web App开发 移动开发 JavaScript
web worker详解
web worker详解
198 0
|
移动开发 JavaScript 前端开发
性能优化之使用vue-worker插件(基于Web Worker)开启多线程运算提高效率
性能优化之使用vue-worker插件(基于Web Worker)开启多线程运算提高效率
30410 1