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);
        }
    }
}

posted @   wanglei1900  阅读(127)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示