You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Web Worker (工作线程) 是 HTML5 中提出的概念,分为两种类型,专用线程(Dedicated Web Worker) 和共享线程(Shared Web Worker)。专用线程仅能被创建它的脚本所使用(一个专用线程对应一个主线程),而共享线程能够在不同的脚本中使用(一个共享线程对应多个主线程)。
专用线程可以看做是默认情况的 Web Worker,其加上修饰词的目的是为了与共享线程进行区分。本文会较为严格地区分两者,可能较为累赘,但个人认为这是必要的。如果单纯以 Web Worker 字样出现的地方指的是两者都会有的情况。
用途
Web Worker 的意义在于可以将一些耗时的数据处理操作从主线程中剥离,使主线程更加专注于页面渲染和交互。
懒加载
文本分析
流媒体数据处理
canvas 图形绘制
图像处理
...
需要注意的点
有同源限制
无法访问 DOM 节点
运行在另一个上下文中,无法使用Window对象
Web Worker 的运行不会影响主线程,但与主线程交互时仍受到主线程单线程的瓶颈制约。换言之,如果 Worker 线程频繁与主线程进行交互,主线程由于需要处理交互,仍有可能使页面发生阻塞
主线程通过 MessagePort 访问专用线程和共享线程。专用线程的 port 会在线程创建时自动设置,并且不会暴露出来。与专用线程不同的是,共享线程在传递消息之前,端口必须处于打开状态。MDN 上的 MessagePort 关于 start() 方法的描述是:
Starts the sending of messages queued on the port (only needed when using EventTarget.addEventListener; it is implied when using MessagePort.onmessage.)
简介
Web Worker (工作线程) 是 HTML5 中提出的概念,分为两种类型,专用线程(Dedicated Web Worker) 和共享线程(Shared Web Worker)。专用线程仅能被创建它的脚本所使用(一个专用线程对应一个主线程),而共享线程能够在不同的脚本中使用(一个共享线程对应多个主线程)。
专用线程可以看做是默认情况的 Web Worker,其加上修饰词的目的是为了与共享线程进行区分。本文会较为严格地区分两者,可能较为累赘,但个人认为这是必要的。如果单纯以
Web Worker
字样出现的地方指的是两者都会有的情况。用途
Web Worker 的意义在于可以将一些耗时的数据处理操作从主线程中剥离,使主线程更加专注于页面渲染和交互。
需要注意的点
浏览器支持度
根据 CanI Use 网站的统计,目前约有 93.05% 的浏览器支持专用线程。
而对于共享线程,则仅有大约 41.66% 的浏览器支持。
由于专用线程和共享线程的构造方法都包含在 window 对象中,我们在使用两者之前可以对浏览器的支持性进行判断。
线程创建
专用线程由
Worker()
方法创建,可以接收两个参数,第一个参数是必填的脚本的位置,第二个参数是可选的配置对象,可以指定type
、credentials
、name
三个属性。共享线程使用
Shared Worker()
方法创建,同样支持两个参数,用法与Worker()
一致。值得注意的是,因为 Web Worker 有同源限制,所以在本地调试的时候也需要通过启动本地服务器的方式访问,使用
file://
协议直接打开的话将会抛出异常。数据传递
Worker 线程和主线程都通过
postMessage()
方法发送消息,通过onmessage
事件接收消息。在这个过程中数据并不是被共享的,而是被复制的。值得注意的是Error
和Function
对象不能被结构化克隆算法复制,如果尝试这么做的话会导致抛出DATA_CLONE_ERR
的异常。另外,postMessage()
一次只能发送一个对象, 如果需要发送多个参数可以将参数包装为数组或对象再进行传递。关于
postMessage()
和结构化克隆算法(The structured clone algorithm)将在本文最后进行阐述。下面是专用线程数据传递的示例。
在 Worker 线程中,
self
和this
都代表子线程的全局对象。对于监听message
事件,以下的四种写法是等同的。主线程通过
MessagePort
访问专用线程和共享线程。专用线程的 port 会在线程创建时自动设置,并且不会暴露出来。与专用线程不同的是,共享线程在传递消息之前,端口必须处于打开状态。MDN 上的MessagePort
关于start()
方法的描述是:这句话经过试验,可以理解为
start()
方法是与addEventListener
配套使用的。如果我们选择onmessage
进行事件监听,那么将隐含调用start()
方法。在传递消息时,
postMessage()
方法和onmessage
事件必须通过端口对象调用。另外,在 Worker 线程中,需要使用onconnect
事件监听端口的变化,并使用端口的消息处理函数进行响应。关闭 Worker
可以在主线程中使用
terminate()
方法或在 Worker 线程中使用close()
方法关闭 worker。这两种方法是等效的,但比较推荐的用法是使用close()
,防止意外关闭正在运行的 Worker 线程。Worker 线程一旦关闭 Worker 后 Worker 将不再响应。错误处理
可以通过在主线程或 Worker 线程中设置
onerror
和onmessageerror
的回调函数对错误进行处理。其中,onerror
在 Worker 的error
事件触发并冒泡时执行,onmessageerror
在 Worker 收到的消息不能进行反序列化时触发(本人经过尝试没有办法触发onmessageerror
事件,如果在 worker 线程使用postMessage
方法传递一个 Error 或 Function 对象会因为无法序列化优先被onerror
方法捕获,而根本不会进入反序列化的过程)。加载外部脚本
Web Worker 提供了
importScripts()
方法,能够将外部脚本文件加载到 Worker 中。子线程
Worker 可以生成子 Worker,但有两点需要注意。
嵌入式 Worker
目前没有一类标签可以使 Worker 的代码像
<script>
元素一样嵌入网页中,但我们可以通过Blob()
将页面中的 Worker 代码进行解析。关于 postMessage
Web Worker 中,Worker 线程和主线程之间使用结构化克隆算法(The structured clone algorithm)进行数据通信。结构化克隆算法是一种通过递归输入对象构建克隆的算法,算法通过保存之前访问过的引用的映射,避免无限遍历循环。这一过程可以理解为,在发送方使用类似
JSON.stringfy()
的方法将参数序列化,在接收方采用类似JSON.parse()
的方法反序列化。但是,一次数据传输就需要同时经过序列化和反序列化,如果数据量大的话,这个过程本身也可能造成性能问题。因此, Worker 中提出了
Transferable Objects
的概念,当数据量较大时,我们可以选择在将主线程中的数据直接移交给 Worker 线程。值得注意的是,这种转移是彻底的,一旦数据成功转移,主线程将不能访问该数据。这个移交的过程仍然通过postMessage
进行传递。例如,传递一个 ArrayBuffer 对象
上下文
Worker 工作在一个
WorkerGlobalDataScope
的上下文中。每一个WorkerGlobalDataScope
对象都有不同的event loop
。这个event loop
没有关联浏览器上下文(browsing context),它的任务队列也只有事件(events)、回调(callbacks)和联网的活动(networking activity)。每一个
WorkerGlobalDataScope
都有一个closing
标志,当这个标志设为true
时,任务队列将丢弃之后试图加入任务队列的任务,队列中已经存在的任务不受影响(除非另有指定)。同时,定时器将停止工作,所有挂起(pending)的后台任务将会被删除。Worker 中可以使用的函数和类
由于 Worker 工作的上下文不同于普通的浏览器上下文,因此不能访问 window 以及 window 相关的 API,也不能直接操作 DOM。Worker 中提供了
WorkerNavigator
和WorkerLocation
接口,它们分别是 window 中Navigator
和Location
的子集。除此之外,Worker 还提供了涉及时间、存储、网络、绘图等多个种类的接口,以下列举了其中的一部分,更多的接口可以参考 MDN 文档。时间相关
Worker 相关
存储相关
网络相关
相关链接
参考
扩展阅读
The text was updated successfully, but these errors were encountered: