webworker(简易——需扩展)
webworker(简易——需扩展)
javascript是单线程工作,浏览器不会同时运行两个事件处理程序。但是浏览器有个work类可以解除单线程的限制。
一、特性
1. Worker运行于独立的运行环境,有着完全独立的全局对象,无法访问window和document对象。
2. Worker与主线程只能通过异步消息机制通信。(意味着仍不可能并发修改DOM)。
3. 创建新的工作线程,意味着可以将大量密集或长时间的计算放入worker线程中,如大文件上传的切片和图像处理等。
4. 开辟了新的线程,意味着Worker的API也分为两部分,主线程的work对象和新开辟工作线程的WorkerGlobalScope.
二、主线程的worker对象
1.let worker = new Worker("utils/***.js")
要创建新的工作线程,直接调用Worker构造函数,传入一个URL,这个URL用于指定线程要执行的js代码,如果传入的url相对路径则会去调用文件的相对位置调用,如果传入的绝对url路径必须保持同源策略(协议、主机、端口相同)。
2. 主线程worker.postMessage(***)
,工作线程self.onmessage=function(e){}
主线程postMessage可以向工作线程发送字符串、对象、数组、定型数组、映射、集合,这里postMessage使用了结构化克隆算法,浅显理解为比JSON深拷贝高级。工作线程通过监听message事件(这里使用DOM0和DOM2事件都可以),传递的消息保存在e.data中。
3.反之工作线程也可以使用postMessage向主线程传递消息,同上。
4.主线程worker.terminate()
可以强制停止工作线程
三、工作线程的全局对象
主线程调用Worker对象时传入的url地址所指向的js代码会与主线程隔离开。全局对象为WorkGlobalScope对象。
1.创建Worker对象时,第二个参数为name属性
该name的值为该工作线程全局对象的name属性,在console.warn()或console.error()都会包含这个名字。
2.工作线程self.close()
工作线程调用close和主线程调用terminate一样终止线程。
3.工作线程的全局对象引用是self
self拥有js全局对象的所有属性,也拥有window对象的以下属性
setTimeout()
、clearTimeout()
、setInterval()
、clearInterval()
等定时器方法- self建议使用DOM2事件
addEventListener()
、removeEventListener()
- self也可以在工作线程中另外开启一个工作线程,类似于现实中的分包。
- location属性描述传入的URL,属性都是只读的。navigator属性类似于window对象的,有appName、appVersion、platform、userAgent和online属性
四、在工作线程中引包。
worker不支持模块系统,但是工作线程有自己的方式。可以传入多个url(js代码文件),按照传入的顺序执行,具有阻塞性。js代码脚本文件也可以使用importScripts。importScripts()是同步函数。只有Chrome中实现模块和import,但是由于兼容性不作介绍。
self.importScripts("url1","url2")
五、工作线程中执行流程和错误
工作线程自下而上同步的执行自己的代码、脚本模块,之后就进入异步阶段,准备对事件和定时器。只要注册了message事件,如果有可能接受消息,则工作线程不会退出。如果没有注册message事件,则将代码中的异步代码执行完毕,工作线程会自动退出。所以尽量不要显式的调用close结束线程,除非和主线程已经协商一致。
如果工作线程中出现了异常,工作线程全局对象错误未处理,会被传播到主线程worker对象上。
// 在工作线程处理错误
self.onerror(e){
e.preventdefault()
}
// 如果工作线程不处理错误,则需要在主线程处理未被捕获的错误
worker.onerror = function(e){
e.preventDefault()
}
六、postMessage()、MessagePort和MessageChannel
MessageChannel是一个对象,有两个属性port1和port2,他们都是MessagePort对象,MessagePort对象有一个postMessage和onmessage事件。在port1的postMessage会触发port2的message事件。这里port2如果使用了DOM2事件,则需要先调用start(),否则会看不看消息。
其中postMessage第一个参数是消息,第二个参数是数组,会被转移到另一端而非复制(性能)。
- 主线程与工作线程之间通信
let worker = new Worker("worker.js")
let urgentChannel = new MessageChannel()
let urgentPort = channel.port1
worker.postMessage({command:"toPort2",value:channel.port2},[channel.port2])
// 接受工作线程发过来的紧急消息
urgentChannel.addEventListener("message",handleUrgentMessage)
// 开始接受消息
urgentChannel.start()
// 发送紧急消息
urgentPort.postMessage("test")
- 工作线程之间直接通信,转移
let worker1 = new Worker('./worker1.js');
let worker2 = new Worker('./worker2.js');
let ms = new MessageChannel();
// 把 port1 分配给 worker1
worker1.postMessage('main', [ms.port1]);
// 把 port2 分配给 worker2
worker2.postMessage('main', [ms.port2]);
worker2.onmessage = function(e) {
console.log(e.data);
}
// worker1.js
self.onmessage = function(e) {
if (e.data === 'main') {
const port = e.ports[0];
port.postMessage('Hi! I'm worker1');
}
}
// worker2.js
self.onmessage = function(e) {
if (e.data === 'main') {
const port = e.ports[0];
port.onmessage = function(e) {
postMessage(e.data);
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!