H5 web Worker
1.概述:
H5
规范提供了js
分线程的实现,取名为:Web Workers。Web Worker是HTML5
提供的一个javascript
多线程解决方案。我们可以将一些大计算量的代码交由web Worker运行而不冻结用户界面,但还是规定只有主线程才能更新界面。
2.特点:
(1)同源限制:
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
(2)DOM限制
Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document
、window
、parent
这些对象。但是,Worker 线程可以navigator
对象和location
对象。
(3)通信联系
Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。
(4)脚本限制
Worker 线程不能执行alert()
方法和confirm()
方法,但可以使用 XMLHttpRequest
对象发出 AJAX 请求。
(5)文件限制
Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://
),它所加载的脚本,必须来自网络。
3.实例操作:
//下面我们通过创建一个分线程来对主线程中耗时的一段程序进行处理
//下面求在斐波那契数列中第十个数的大小
<input type="text" id="number" value="50">
var value=document.getElementById('number').value;
function Fibo(num){
return num<=2? 1:Fibo(num-1)+Fibo(num-2);
}
console.log(Fibo(value));
//可是当数值很大的时候,需要很长时间的计算才能响应,这个期间将无法操作浏览器,所以这时候使用分线程来解决这个问题。
//主线程
<input type="text" id="number" value="10">
var value=document.getElementById('number').value;
var worker=new Worker('worker.js');
//绑定接收消息的监听
worker.onmessage=function(event){
console.log('从分线程返回的值'+event.data);
//主线程可以关闭分线程
worker.terminate();
}
console.log('主线程交给分线程'+value);
worker.postMessage(value);
//主线程可以监听Worker是否发生错误,如果发生错误,Worker会出啊发主线程的error事件
worker.oneror(function(event){
console.log([
'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message
].join(''))
})
//分线程 worker.js
//加载脚本
importScript("./script.js");
//js中使用闭包实现模块思想 这里实现了es6和node.js中同样的模块思想
function Fibo(num){
return num<=2? 1:Fibo(num-1)+Fibo(num-2);
}
//接收主线程的数据
//这里可以使用onmessage的方式监听,也可以使用addListener()的方式绑定监听事件
this.onmessage=function(event){
var number=event.data;
var result=Fibo(number)
console.log('数据处理后分线程的值'+result);
//将数据传给主线程
postMessage(result);
//用于在Worker内部关闭自身。
self.close();
}
//脚本 script1.js
function script1(){
console.log('我是script1.js');
}
4.数据通信:
前面说过,主线程与 Worker 之间的通信内容,可以是文本,也可以是对象。需要注意的是,这种通信是拷贝关系,即是传值而不是传址,Worker 对通信内容的修改,不会影响到主线程。事实上,浏览器内部的运行机制是,先将通信内容串行化,然后把串行化后的字符串发给 Worker,后者再将它还原。
但是,拷贝方式发送二进制数据,会造成性能问题。比如,主线程向 Worker 发送一个 500MB 文件,默认情况下浏览器会生成一个原文件的拷贝。为了解决这个问题,JavaScript 允许主线程把二进制数据直接转移给子线程,但是一旦转移,主线程就无法再使用这些二进制数据了,这是为了防止出现多个线程同时修改数据的麻烦局面。这种转移数据的方法,叫做Transferable Objects。这使得主线程可以快速把数据交给 Worker,对于影像处理、声音处理、3D 运算等就非常方便了,不会产生性能负担。
//主线程
var worker=new Worker('./worker.js');
//绑定接收消息的监听
worker.onmessage=function(event){
console.log('从分线程返回的值'+event.data);
}
var array = new ArrayBuffer(10);
//转移数据控制权的写法
// Transferable Objects 格式
worker.postMessage(array,[array]);
5、同页面的Web Worker
通常情况下,Worker 载入的是一个单独的 JavaScript 脚本文件,但是也可以载入与主线程在同一个网页的代码。
//注意必须指定<script>标签的type属性是一个浏览器不认识的值,上例是app/worker。
<script id="worker" type="app/worker">
onmessage = function(event) {
console.log("收到主线程的数据:", event);
postMessage("done!");
}
</script>
<script>
var blob = new Blob([document.querySelector("#worker").textContent]);
var url = window.URL.createObjectURL(blob);
var worker = new Worker(url);
worker.onmessage = function(event) {
console.log("收到worker的数据:", event);
worker.terminate();
};
worker.postMessage("start");
</script>
//先将嵌入网页的脚本代码,转成一个二进制对象,然后为这个二进制对象生成 URL,再让 Worker 加载这个 URL。这样就做到了,主线程和 Worker 的代码都在同一个网页上面。
6.实例:Worker线程完成轮询
有时,浏览器需要轮询服务器状态,以便第一时间得知状态改变。这个工作可以放在 Worker 里面。
//参数是一个轮询函数,先将函数转成一个二进制对象,然后为这个对象生成URL,再让Worker架子啊这个URL。此时的轮询操作就在分线程中。
function createWorker(f) {
var blob = new Blob(['(' + f.toString() +')()']);
var url = window.URL.createObjectURL(blob);
var worker = new Worker(url);
return worker;
}
//执行函数
var pollingWorker = createWorker(function (e) {
var cache;
//将返回的数据和缓存作比较
function compare(new, old) { ... };
setInterval(function () {
fetch('/my-api-endpoint').then(function (res) {
var data = res.json();
if (!compare(data, cache)) {
cache = data;
//如果数据发生了变化,则将新数据返回给主线程
self.postMessage(data);
}
})
}, 1000)
});
pollingWorker.onmessage = function (event) {
//监听分线程传过来的数据,也就是改变后的数据
console.log(event.data);
}
pollingWorker.postMessage('init');
7.API
主线程:浏览器原生提供了Worker()构造函数,用来供主线程生成Worker线程
Worker()
构造函数,可以接受两个参数。第一个参数是脚本的网址(必须遵守同源政策),该参数是必需的,且只能加载 JS
脚本,否则会报错。第二个参数是配置对象,该对象可选。它的一个作用就是指定 Worker 的名称,用来区分多个 Worker 线程。
// 主线程
var myWorker = new Worker('worker.js', { name : 'myWorker' });
// Worker 线程
self.name // myWorker
Worker线程
Web Worker 有自己的全局对象,不是主线程的window
,而是一个专门为 Worker 定制的全局对象。因此定义在window
上面的对象和方法不是全部都可以使用。