JS学习-Web Worker

Web Worker

在专用workers的情况下,DedicatedWorkerGlobalScope 对象代表了worker的上下文(专用workers是指标准worker仅在单一脚本中被使用;共享worker的上下文是SharedWorkerGlobalScope (en-US)对象)。一个专用worker仅仅能被首次生成它的脚本使用,而共享worker可以同时被多个脚本使用。在一个worker中最主要的你不能做的事情就是直接影响父页面。包括操作父页面的节点以及使用页面中的对象。你只能间接地实现,通过DedicatedWorkerGlobalScope.postMessage (en-US)回传消息给主脚本,然后从主脚本那里执行操作或变化。

Web Workers可以使用的函数和类

  1. 比较不同类型workers的属性和方法
  2. APIs available in workers

数据收发

在主页面与 worker 之间传递的数据是通过拷贝,而不是共享来完成的。传递给 worker的对象需要经过序列化,接下来在另一端还需要反序列化。页面与worker不会共享同一个实例,最终的结果就是在每次通信结束时生成了数据的一个副本。大部分浏览器使用结构化拷贝来实现该特性。

结构化拷贝不能拷贝的情况:

  • Error以及Function对象是不能被结构化克隆算法复制的;如果你尝试这样子去做,这会导致抛出 DATA_CLONE_ERR的异常。
  • 企图去克隆DOM上游同样会引发DATA_CLONE_ERROR异常。
  • 对象的某些特定参数也不会被保留
    • RegExp对象的lastIndex主轴不会被保留
    • 例如,如果一个对象使用属性标记为readonly,则它将被复制为read-write,因为这是默认的情况下。
    • 原形链上的属性也不会被追踪以及复制。

专用worker

特性检测
if (window.Worker) { ... }
引入脚本与库
importScripts(); /* 什么都不引入 */
importScripts('foo.js'); /* 只引入 "foo.js" */
importScripts('foo.js', 'bar.js'); /* 引入两个脚本 */
生成专用worker
// main.js
var myWorker = new Worker('worker.js');
消息收发
// postMessage
myWorker.postMessage([first.value,second.value]);
// onmessage
myWorker.onmessage = function(e) {
	result.textContent = e.data;
	console.log('Message received from worker');
}// worker.js
onmessage = function(e) {
	console.log('Message received from main script');
	postMessage('Result: ' + (e.data));
}
终止worker
// main.js
myWorker.terminate();
// worker.js
close()
处理错误
onerror = function(e){
	console.log(e)
}// e:{message:'',filename:'',lineno(行号):''}

共享worker

生成共享worker
// worker.js
port.start();
// main.js
var myWorker = new SharedWorker('worker.js');
myWorker.port.start(); // 父级线程中的调用
消息收发
// worker.js
onconnect = function(e) {
	var port = e.ports[0];
	port.onmessage = function(e) {
		port.postMessage('Result: ' + (e.data));
	}
}
//main.js
myWorker.port.postMessage([squareNumber.value]);
myWorker.port.onmessage = function(e) {
	result2.textContent = e.data;
	console.log('Message received from worker');
}

嵌入式 worker

<script type="text/js-worker">
	// 该脚本不会被 JS 引擎解析,因为它的 mime-type 是 text/js-worker。
	var myVar = "Hello World!";
	// 剩下的 worker 代码写到这里。
</script>

<script type="text/javascript">
	function pageLog (sMsg) {
		// 使用 fragment:这样浏览器只会进行一次渲染/重排。
		var oFragm = document.createDocumentFragment();
		oFragm.appendChild(document.createTextNode(sMsg));
		oFragm.appendChild(document.createElement("br"));
		document.querySelector("#logDisplay").appendChild(oFragm);
	}
</script>

<script type="text/js-worker">
	onmessage = function (oEvent) {
		postMessage(myVar);
	};
	// 剩下的 worker 代码写到这里。
</script>

<script type="text/javascript">
	// 该脚本会被 JS 引擎解析,因为它的 mime-type 是 text/javascript。
	// ...使用 Blob...:
	var queryWorker = Array.prototype.map.call(
		document.querySelectorAll("script[type=\"text\/js-worker\"]";
		var queryWorkerContent = queryWorker,
		function (oScript) {
			return oScript.textContent;
		})
	)
	var blob = new Blob(queryWorkerJs,{type: "text/javascript"});
	// 创建一个新的 document.worker 属性,包含所有 "text/js-worker" 脚本。
	document.worker = new Worker(window.URL.createObjectURL(blob));
	document.worker.onmessage = function (oEvent) {
		pageLog("Received: " + oEvent.data);
	};
	// 启动 worker.
	window.onload = function() {
		document.worker.postMessage("");
	};
</script>

可以将一个函数转换为blob,然后为这个blob生成URL对象。

function fn2workerURL(fn) {
	var blob = new Blob(['('+fn.toString()+')()'], {type: 'application/javascript'})
	return URL.createObjectURL(blob)
}

内容安全策略

为了给worker指定内容安全策略,必须为发送worker代码的请求本身加上一个 内容安全策略。

传递数据的例子

  1. 创建一个通用的 「异步 eval()」
  2. 传输 JSON 的高级方式和创建一个交换系统
  3. 在后台执行运算

通过转让所有权(可转让对象)来传递数据

/* 当你将一个 ArrayBuffer 对象从主应用转让到 Worker 中,原始的 ArrayBuffer 被清除并且无法使用。它包含的内容会(完整无差的)传递给 Worker 上下文。 */
// Create a 32MB "file" and fill it.
var uInt8Array = new Uint8Array(1024*1024*32); // 32MB
for (var i = 0; i < uInt8Array .length; ++i) {
	uInt8Array[i] = i;
}//uInt8Array.buffer ArrayBuffer(33554432)
worker.postMessage(uInt8Array.buffer, [uInt8Array.buffer]);
posted @ 2022-08-12 15:08  ~LemonWater  阅读(317)  评论(0编辑  收藏  举报