简介
我们都知道JavaScript这个语言在执行的时候是采用单线程进行执行的,也就是说在同一时间只能做同一件事,这也和这门语言有很大的关系,采用同步执行的方式进行运行,如果出现阻塞,那么后面的代码就不会执行,HTML5则提出了webWorker标准,表示JavaScript允许有多个线程,但是子线程完全受主线程的控制,切子线程不能操作DOM,只有主线程可以操作DOM,所以主线程为主的单线程执行原理乘客JavaScript这门语言的核心。简单来说,webWorker就是在JavaScript单线程执行的基础上,开启一个子线程,进行程序处理,而不影响主线程的执行,当子线程执行完毕之后再回到主线程上,在这个过程中并不影响主线程的执行过程。
举个例子:
传统情况下,执行下面的代码后,整个页面都会被冻结,由于JavaScript是单线程处理,如下代码已经完全阻塞了后续的执行。
while(true){}
如果换一种方式,我们通过开启一个新的线程来执行这段代码,将他放在一个单独的worker.jswenj文件中,在主线程执行以下代码。
var worker = new Worker("worker.js")
创建线程
在创建线程的时候需要给实例化的Worker
传入唯一一个参数,指向一个javascript
文件资源的url或者Blob对象(Blob对象就是一个包含有只读原始数据类文件对象),调用这个构造函数之后,一个线程就被创建了,如下:
var worker = new Worker("worker.js");
var worker = new Worker(blob);
线程通信
Web Worker
的基本原理就是在当前的主线程中加载一个只读文件来创建一个新的线程,两个线程同时存在,且互不阻塞,并且在子线程与主线程之间提供了数据交换的接口postMessage
和onmessage
。来进行发送数据和接收数据。其数据格式可以为结构化数据(JSON
等);
当我们创建了一个worker实例之后,我们可以通过如下两种方式来发送数据:
var worker = new Worker("worker.js"); //实例化对象
//第一种传递方式
worker.postMessage(message,taransferList);
worker.postMessage({},[]);
//第二种传递方式
worker.postMessage({
operation: "list_all_users",
//ArrayBuffer object 数组缓冲对象
input: buffer,
threshold: 0.8,
}, [buffer]);
如果要想一个专用线程发送数据,那么我们需要使用线程中的 postMessage 方法。专用线程不仅仅支持传输二进制数据,也支持结构化的 JavaScript 数据格式。在这里有一点需要注意,为了高效地传输 ArrayBuffer 对象数据,需要在 postMessage 方法中的第二个参数中指定它。
同时我们如果需要接收某个线程传来的数据可以使用onmessage
来进行接收,方法如下:
//方法一
worker.onmessage = function(event){
var data = event.data; //通过event.data来获取传入的参数
}
//方法二
worker.addEventListener("message",target); //该事件通过或者从对象(WebSocket, Web Worker, Event Source 或者子 frame 或父窗口)接收到消息时触发
下面是一段运行在chrome中的参数传递方式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webWorker</title>
</head>
<body>
<script>
var worker = new Worker("worker.js");
worker.postMessage("123456");
worker.onmessage = function (e) {
console.log(e.data)
};
</script>
</body>
</html>
onmessage = function (e) {
console.log(e.data);
postMessage("2222")
};
Worker基本使用
创建新的Worker
var worker = new Worker("worker.js")
传递参数
worker.postMessage("text");
接收消息
worker.onmessage = function (e) {
var message = e.data;
};
异常处理
worker.onerror = function(e){
console.log("error at "+e.filename ":" + e.lineno + e.message)
}
结束worker
worker.terminate();
载入工具类函数
importScripts("./utils/base64.js","./utils/map.js"...)
注:importScripts
是同步方法,一旦importScripts
方法返回就可以开始使用载入的脚本,而不需要回调函数。
Worker作用域
当我们创建一个新的worker时,改代码会运行在一个全新的javascript的环境中(WorkerGlobalScope)运行,是完全和创建worker的脚本隔离,这时我们可以把创建新worker的脚本叫做主线程,而被创建的新的worker叫做子线程。
web Worker的使用限制
- Web Worker无法访问DOM节点;
- Web Worker无法访问全局变量或是全局函数;
- Web Worker无法调用alert()或者confirm之类的函数;
- Web Worker无法访问window、document之类的浏览器全局变量;
不过Web Worker中的Javascript依然可以使用setTimeout(),setInterval()之类的函数,也可以使用XMLHttpRequest对象来做Ajax通信。
两种Web Worker
web Worker可分为两种类型:专用线程dedicated web worker,以及共享线程shared web worker。 Dedicated web worker随当前页面的关闭而结束;这意味着Dedicated web worker只能被创建它的页面访问。与之相对应的Shared web worker可以被多个页面访问。在Javascript代码中,“Work”类型代表Dedicated web worker,而“SharedWorker”类型代表Shared web worker。
在绝大多数情况下,使用Dedicated web worker就足够了,因为一般来说在web worker中运行的代码是专为当前页面服务的。而在一些特定情况下,web worker可能运行的是更为普遍性的代码,可以为多个页面服务。在这种情况下,我们会创建一个共享线程的Shared web worker,它可以被与之相关联的多个页面访问,只有当所有关联的的页面都关闭的时候,该Shared web worker才会结束。相对Dedicated web worker,shared web worker稍微复杂些。
var worker = new Worker("script/lengthytask.js") //Dedicated Web Worker
var worker = new SharedWorker("script/lengthytask.js") //Shared Web Worker
共享线程 SharedWorker
使用SharedWorker
创建共享线程,也需要提供一个javascript脚本文件的URL地址或Blob,该脚本文件中包含了我们在线程中需要执行的代码,如下:
var worker = new SharedWorker("sharedworker.js");
共享线程也使用了message
事件监听线程消息,但使用SharedWorker对象的port属性与线程通信如下。
worker.port.onmessage = function(e){
...
}
同时我们也可以使用SharedWorker对象的port属性向共享线程发送消息如下。
worker.port.postMessage("message");