本文是读《你不知道的JavaScript中》后所做的记录,如有内容有误欢迎指正。 《你不知道的JavaScript中》读书笔记
Web Worker 启动多线程
Work使用场景
Web Worker 通常应用于哪些方面呢?
- 处理密集型数学计算
- 大数据集排序
- 数据处理(压缩、音频分析、图像处理等)
- 高流量网络通信
Work创建及终止
实例化
//1. 实例化woker,
var w1 = new Worker( "http://some.url.1/mycoolworker.js" );
-
这个 URL 应该
指向一个 JavaScript 文件的位置(而不是一个 HTML 页面!),这个文件将被加载到一个 Worker 中。然后浏览器启动一个独立的线程,让这个文件在这个线程中作为独立的程序运行。 -
如果浏览器中有两个或多个页面(或同一页上的多个 tab !)试图从同一个文件 URL 创建 Worker,那么最终得到的实际上是完全独立的 Worker
数据收发
Worker 之间以及它们和主程序之间,不会共享任何作用域或资源,那会把所有多线程编程的噩梦带到前端领域,而是通过一个基本的事件消息机制相互联系`;
//2. 数据的获取(主线程发送消息给woker)
w1.addEventListener( "message", function(evt){
// evt.data
} );
//数据的发送(主线程收到woker的信息)
w1.postMessage( "something cool to say" );
// mydemo_woker.js
//在worker内部,监听主线程发给自己的信息
addEventListener( "message", function(evt){
// evt.data
} );
//发送数据给主线程
postMessage( "a really cool reply" );
注意,专用 Worker 和创建它的程序之间是一对一的关系。
通常由
主页面应用程序创建 Worker,但若是需要的话,Worker也可以实例化它自己的子 Worker,称为 subworker。有时候,把这样的细节委托给一个“主”Worker,由它来创建其他 Worker 处理部分任务,这样很有用。不幸的是,到写作本书时为 止,Chrome 还不支持 subworker,不过 Firefox 支持。
Work的终止
//主页面手动终止
w1.terminate();
突然终止Worker 线程不会给它任何机会完成它的工作或者清理任何资源。这就类似于通过关闭浏览器标签页来关闭页面。
Work运行环境及外部脚本加载
Work不可以访问主程序的任何资源,不能访问dom或者其他资源,但是可以执行网络操 作(Ajax、WebSockets)以及设定定时 器。还 有,Worker 可以访问几个重要的全局变量和功能的本地复 本,包括 navigator 、location 、JSON 和 applicationCache
可以通过 importScripts(..) 向 Worker 加载额外的 JavaScript 脚本:
// 在Worker内部
importScripts( "foo.js", "bar.js" );
这些脚本加载是同步的。也就是说,importScripts(..) 调用会阻塞余下 Worker 的执行,直到文件加载和执行完成。
Work数据传递
在早期的 Worker 中,唯一的选择就是把所有数据序列化到一个字符串值中,然后使用postMessage发送并使用addEventListener("message")的方式获取数据
方法一:使用Transferable对象
特别是对于大数据集而言,就是
使用 Transferable 对象(updates.html5rocks.com/2011/12/Tra… )。
- 这时发生的是对象所有权的转移,数据本身并没有移动。一旦你把对象传递到一个 Worker 中,在原来的位置上,它就变为空的或者是不可访问的,这样就消除了多线程编程作用域共享带来的混乱。当然,所有权传递是可以双 向进行的。
- 如果
选择 Transferable 对象的话,其实不需要做什么。任何实现了 Transferable 接口(developer.mozilla.org/en-US/docs/… )的数据结构就自动按照这种方式传输(Firefox 和 Chrome 都支持)。
使用结构化克隆算法
如果要传递一个对象,可以使用结构化克隆算法 (structured clone algorithm)(developer.mozilla.org/en-US/docs/… )把这个对象复制到另一边。这个
算法非常高级,甚至可以处理要复制的对象有循环引用的情况。这样就不用付出 to-string 和 from-string 的性能损失了,但是这种方案还是要使用双倍的内存。IE10 及更高版本以及所有其他主流浏览器都支持这种方案。
共享Worker
常见情况:前面已经讲过每一个worker是独立的,但为了节省系统资源(在这一方面最常见的有限资源就是 socket 网络连接,因为浏览器限制了到同一个主机的同时连接数目。(2-8个之间,通常为6个))
主页面实例化共享Woker
//创建一个整个站点或 app 的所有页面实例都可以共享的中心 Worker
var w1 = new SharedWorker( "http://some.url.1/mycoolworker.js" );
/**
因为共享 Worker 可以与站点的多个程序实例或多个页面连接,所以这个 Worker 需要通过某种方式来得知消息来自于哪个程
序。这个唯一标识符称为端口 (port),可以类比网络 socket 的端口。因此,调用程序必须使用 Worker 的 port 对象用于通
信:
**/
w1.port.addEventListener( "message", handleMessages );
// ..
w1.port.postMessage( "something cool" );
// 端口连接必须要初始化
w1.port.start();
共享Worker内部
共享 Worker 内部,
必须要处理额外的一个事件:"connect"。这个事件为这个特定的连接提供了端口对象。保持多个连接独立的最简单办法就是使用 port 上的闭包
// 在共享Worker内部
addEventListener( "connect", function(evt){
// 这个连接分配的端口
var port = evt.ports[0];
port.addEventListener( "message", function(evt){
// ..
port.postMessage( .. );
// ..
} );
// 初始化端口连接
port.start();
} );
共享Worker和Worker的区别
如果有某个端口连接终止而其他端口连接仍然活跃,那么共享 Worker 不会终止。而对专用 Worker 来说,只要到实例化它的程序的连接终止,它就会终止