什么是Worker?他有什么作用?
因为js是单线程运行的,在遇到一些需要处理大量耗时的数据时,可能会阻塞页面的加载,造成面的假死。针对这个问题,H5出了新的功能worker,支持多线程,这时我们可以使用worker来开辟一个独立于主线程的子线程来进行一些耗时的大量运算。就可以用来解决因为大量耗时运算是造成页面假死的问题,详细内容查看.
如何使用Worker
//首先创建 Worker 对象,第一个参数 worker 需要执行的 JS 文件的 URL。相对地址 URL 是相对于创建 Worker 的脚本所在文档(宿主文档)的访问地址。绝对地址 URL 必须要和宿主文档同源。
//第二个参数是可选参数,可以是一个对象,name属性,可以给当前Worker定义一个名称,可用来区分其他的worker
const worker = new Worker("worker.js",{name:'自定义的名称'})
//通过onmessage ,监听子线程的消息
worker.onmessage((e)=>`接收worker线程传递值:${e.data}`)
//通过postMessage ,向子线程发送消息
worker.postMessage('向子线程发送消息')
//worker.js 文件
//通过self.onmessage,来接收主线程传递的信息
self.onmessage((e)=>{
console.log(e)
})
//通过self.postMessage,来向主线程传递信息
self.postMessage('要传递的值')
方法和属性
主线程worker()对象,用来供主线程操作 Worker。worker 线程对象的属性和方法如下:
- worker.onerror:指定 error 事件的监听函数。
- worker.onmessage:指定 message 事件的监听函数,发送过来的数据在Event.data属性中。
- worker.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
- worker.postMessage():向 Worker 线程发送消息。
- worker.terminate():立即终止 Worker 线程。
worker 子线程全局属性和方法:
- self.onmessage:指定message事件的监听函数。
- self.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
- self.close():关闭 worker 线程。
- self.postMessage():向产生这个 worker 线程发送消息。
- self.importScripts():加载 JS 脚本。
- self.name: worker 的名字。该属性只读,由构造函数指定。
worker 使用注意点
- 同源限制
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
- DOM 限制
worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。
- 通信联系
worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息传递完成。
- 脚本限制
worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。
- 文件限制
worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。
简单使用demo
模拟页面中插入耗时处理数据然后渲染元素,暂时不考虑页面渲染性能问题
(() => {
let arrList = []
let indexNum = 1000
for (let index = 0; index < indexNum; index++) {
let obj = {
name: index + 1
}
arrList.push(obj)
if (arrList.length == indexNum) {
//在子线程中,self代表worker子线程的全局对象,也可以用this代替self,或者省略也行
//this.postMessage(arrList);
//self.postMessage(arrList);
postMessage(arrList); //向主线程发送消息
}
onmessage = function(e) {
console.log(e.data)
console.log("我是worker线程接收到了你的信息,不客气,我就是做耗时操作的")
console.log("2妙后我还会给你发送个信息")
setTimeout(() => {
postMessage('如果还有耗时的操作,你还可以在开启一个worker线程呦')
}, 2000)
}
//worker自己关闭
// self.close()
}
})()
<!DOCTYPE html>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<html>
<body>
<div id="bt">
<p style="color:red">模拟元素1</p>
<p style="color:red">模拟元素2</p>
<p style="color:red">模拟元素3</p>
<p style="color:red">模拟元素4</p>
<div class="ld" style="color:green">数据加载中。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。</div>
<div class="main"></div>
<p style="color:red">模拟元素5</p>
<p style="color:red">模拟元素6</p>
<p style="color:red">模拟元素7</p>
</div>
<script>
// 引入的js文件必须和当前页面同源,不可跨域
let w = new Worker("./worker2.js");
$(".ld").show()
// console.log("Worker线程处理完耗时操作后,主线程接会收到处理数据")
w.onmessage = function(e) {
let listData = e.data
console.log(listData)
let strs = ''
for (let j = 0; j < listData.length; j++) {
const element = listData[j];
strs += '<p>' + '这是接收到的数据' + j + '</p>'
}
$(".main").append(strs)
$(".ld").hide()
}
w.postMessage('你发送的数据我收到了,辛苦了')
//主线程关闭子线程
// w.terminate()
</script>
</body>
</html>
SharedWorker一种特殊的worker,详细内容查看:SharedWorker
sharedWorker 顾名思义,是 worker 的一种,可以由所有同源的页面共享。同Worker的api一样,传入js的url,就可以注册一个 sharedWorker 实例, sharedWorker通过port来发送和接收消息
let myWorker = new SharedWorker('worker.js');
//主线程和共享线程都是通过 postMessage() 方法来发送消息
myWorker.port.postMessage('发送消息');
//接收消息都是使用onmessage = (e)=>{},或者 addEventListener('message', (e)=>{})
myWorker.port.onmessage = (e)=>{console.log(e.data)} ;
//关闭启动和worker是有点区别的
线程通过 worker.port.start() 启动
线程通过 worker.port.close() 关闭
SharedWorker使用注意要点同worKer一样
简单使用Demo
- shareWk.js 共享线程,里面存储计数器counter
- A.html 刷新页面,通知counter+1
- B.html 刷新页面,查看counter
// 计时器
let counter = 0
// 监听连接
self.addEventListener('connect', (e) => {
const port = e.ports[0]
port.onmessage = (res) => {
console.log('A,B页面共享的信息:', res.data)
switch (res.data) {
case 'add':
counter++
break
}
console.log('counter:', counter)
port.postMessage(counter)
}
})
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>A页面</title>
</head>
<body>
<h1>A页面</h1>
<script>
// 兼容性判断
if (!SharedWorker) {
throw new Error('当前浏览器不支持SharedWorker')
}
// 创建共享线程
const worker = new SharedWorker('shareWk.js')
// 启动线程端口
worker.port.start()
// 向共享线程发送消息
worker.port.postMessage('add')
// 线程监听消息 2种方式都可以
// worker.port.addEventListener('message', (e) => {
// console.log('A页面共享线程counter值:', e.data)
// })
worker.port.onmessage = (e) => {
console.log('A页面取到counter值:', e.data)
}
console.log(worker.port)
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>B页面</title>
</head>
<body>
<h1>B页面</h1>
<script>
// 兼容性判断
if (!SharedWorker) {
throw new Error('当前浏览器不支持SharedWorker')
}
// 创建共享线程
const worker = new SharedWorker('shareWk.js')
// 启动线程端口
worker.port.start()
// 向共享线程发送消息
worker.port.postMessage('get counter')
// 线程监听消息
// 线程监听消息 2种方式都可以
// worker.port.addEventListener('message', (e) => {
// console.log('B页面共享线程counter值:', e.data)
// })
worker.port.onmessage = (e) => {
console.log('B页面取到counter值:', e.data)
}
</script>
</body>
</html>
SharedWorker调试注意点
SharedWorker不能直接在页面调试面板查看,需要浏览器输入 chrome://inspect 然后点击inspect查看