worker介绍
worker是独立于web主线程的,在后台运行的线程。
web worker的优点就是可以将工作交给独立的其他线程去做,这样就不会阻塞主线程。
浏览器端js是单线程执行,所以当js执行高负载运算时,UI渲染就会阻塞,页面就会出现卡顿,用户体验就不是很好.
js为此也提供了异步操作,例如: 定时器(setTimeout 和 setInterval),Ajax请求等,但异步终究还是单线程,不能从根本上解决问题,像setTimeout并不能拿到正确的值,因为执行的时候将该任务放到主线程执行,只有当前面的任务执行完才开始执行,所以即使设置时间为0,也并不一定立刻执行.
所以HTML5标准添加了Web Worker,worker允许一段js程序运行在主线程之外的线程,子线程与主线程互不干扰,当子线程的代码执行完成,将结果返回给主线程,主线程接收到相应结果后再做对应处理,当有一些计算密集型或高延迟的任务就可以交给worker子线程来负担,主线程就不会阻塞.
worker使用
worker是window对象的一个方法,一个worker对象可通过构造函数(window.worker())来创建,创建对象时,需要传给构造函数一个js文件,该文件包含了放到子线程中运行的代码。
worker通过postMessage()和onmessage = () => {} 来进行通信,主线程和子线程通信是双向的,都有可以发送和接收信息,postMessage传递的数据都是拷贝传递(ArrayBuffer除外)
子线程运行耗损系统资源,所以当执行完毕后,我们可以手动关闭子线程
在主线程中关闭: worker.terminate()
在子线程中关闭: self.close() (在子线程中window需改写成self,下文有解释)
worker限制
1)同源
分配给worker子线程运行的脚本文件,必须与主线程的脚本文件同源,包括协议,域名和端口,不支持本地地址(file://);
当使用CDN来存储js文件时,主线程与子线程就会出现跨域
解决方法: 【1】将子线程的脚本转换成Blod对象
【2】然后给Blod对象创建一个URL
【3】将这个URL传给worker的构造函数
示例代码:
var workerBlob = new Blob(['(' + function +')()'], { type: "text/javascript" });
var url = window.URL.createObjectURL(workerBlob);
var worker = new Worker(url);
2) 访问
worker子线程所在的全局对象,与主线程不在一个上下文环境,所以无法读取主线程所在网页的DOM,无法使用document,window,parent这些对象,global指向有变更,window需要改写成self,不能执行alert()和comfire()的方法,只能读取部分navigator对象内的数据
3)使用异步
worker子线程中支持js的异步操作,但仍然不可以跨域
worker的兼容性还不错,但不兼容IE9
以上内容参考:https://www.cnblogs.com/yezi-dream/p/10202593.html
案例:worker解决谷歌浏览器切换页面定时器停止
浏览器为了性能考虑,在你切换完选项卡之后会清除掉这里的缓存数据,这样会导致切换页面定时器会停止,比如:如果做一个在线考试系统,如果用户切换浏览器计时停止就达不到控制时间的目的。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
计时器<span id="countNum">0</span>
<script type="text/javascript">
var countEle = document.getElementById("countNum");
var i = 0;
setInterval(function(){
i++;
countEle.innerText = i;
},1000);
</script>
</body>
</html>
打开谷歌浏览器,切出当前页面,过几分钟后再看,可以发现时间有延迟和停止现象。
解决:worker多线程。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
计时器<span id="countNum">0</span>
<script type="text/javascript">
var countEle = document.getElementById("countNum");
var i = 0;
//setInterval(function(){
// i++;
// countEle.innerText = i;
//},1000);
var blob = new Blob(['(function(e){setInterval(function(){this.postMessage(null)},1000)})()']);
var url = window.URL.createObjectURL(blob);
var work = new Worker(url)
work.onmessage = function(e){
i++;
countEle.innerText = i;
if(i == 60)
{
work.terminate();
}
}
</script>
</body>
</html>