工作者线程
JavaScript单线程,意味着它不可以像 多线程语言那样把工作委托给 独立的线程和进程去做,但是单线程可以保证它与 不同浏览器API兼容。而且多线程意味着它与DOM这样的API就会出现问题
工作者线程:允许把主线程的工作转嫁给独立的实体,而不会改变现有的单线程模型。
各种工作者线程有不同的形式和功能,但他们都是独立于JavaScript的主执行环境。
JavaScript环境实际上是运行在托管操作系统中的虚拟环境。在浏览器中每打开一个页面,就会分配一个它的环境。这样每个页面都有自己的内存、事件循环、DOM等等,相当于一个沙盒。
对于浏览器来说,同时管理多个环境是非常简单的,因为这些环境是并行执行的。
使用工作者线程,浏览器就可以在原始页面环境之外再分配一个完全独立的二级子环境。该子环境不能与依赖单线程交互的API(如DOM)互操作,但可以与父环境并行执行代码。
Document、window对象不能在后台线程中访问,可以访问 web storage
JavaScript线程的各方面包括生命周期管理,代码路径、I/O都是由初始化线程时提供的脚本来控制,该脚本也可以再请求其他脚本,但一个线程总是从一个脚本源开始。
特点:
工作者线程是以实际线程实现的 例如,Blink浏览器引擎实现工作者线程的workerThread就对应着底层线程。
工作者线程并行执行。 虽然页面和工作者线程都是单线程JavaScript环境,每个环境中的指令则可以并行执行。
工作者线程可以共享部分内存。 工作者线程能够使用SharedArrayBuffer在多个环境内共享内容。虽然线程使用锁实现并发控制,但JavaScript使用Atomics接口实现并发控制。
工作者线程不一定在同一个进程。 通常一个进程可以在内部产生多个线程。根据浏览器引擎的实现,工作者线程可能与页面属于同一进程,也可能不属于。
创建工作者线程的开销更大。 工作者线程有独立的事件循环、全局对象、事件处理程序和其他JavaScript环境必需的特性。
工作者线程的类别
专用工作者线程 Web Worker
可以让脚本单独创建一个JavaScript线程,以执行委托的任务。专用工作者线程,只能被创建它的页面使用。
共享工作者线程
可以让脚本单独创建一个JavaScript线程,以执行委托的任务。
共享工作者线程可以被多个不同的上下文使用,包括不同的页面。任何与创建共享工作者线程的脚本同源的脚本,都可以向共享工作者线程发送消息或从中接受消息。
服务工作者线程
特殊
用于拦截、重定向和修改页面发出的请求,充当网络请求的仲裁者的角色。
WorkerGlobalScope对象(子)
在网页上,window对象可以向运行在其中的脚本暴露各种全局变量,在工作者线程内部,这里的全局变量是WorkerGlobalScope的实例,通过self关键子暴露出来。
self上可用属性和方法是window对象上属性和方法的严格子集,其中一些属性会返回特定于工作者线程的版本。
- navigation
- location
- performance
- console
- caches
- indexDB
- isSecureContext
- origin
- atob()
- btoa()
- clearInterval()
- clearTimeout()
- fetch()
- setInterval()
- setTimeout()
此外,WorkerGlobalScope增加新的全局importScripts(),只在工作者线程内使用
各种工作线程类型都有自己的全局对象,继承自WorkerGlobalScope
专用工作者DedicatedWorkerGlobalScope
共享工作者SharedWorkerGlobalScope
服务工作者ServiceWorkerGlobalScope
工作者线程的沙盒
如果工作者线程抛出了错误,该工作者线程沙盒可以阻止它打断父线程的执行,也就是说在父线程中的try/catch语句中定义工作者线程,不会捕获到错误。
相应的错误事件会冒泡到工作者线程的全局上下文,因此可以通过在对象上设置错误事件侦听器访问。
一、专用工作者线程(后台脚本) Worker
网页中的脚本可以创建专用工作者线程来执行在页面线程之外的其他任务。这样的线程可以与父页面交换信息、发送网络请求、执行文件I/O、密集计算、处理数据,以及其他不适合在页面执行线程做的任务(以防页面响应迟钝)
//方法一:脚本文件
console.log(location.href);//https://example.com/
const worker=new Worker(location.href+'newWork.js');//更常用的应该是相对路径
//worker返回通信连接点
console.log(worker);//Worker{}
//方法二:行内创建
//1.创建要执行的代码,注意反引号
//也可以使用函数序列化来实现脚本,function.toString()但是!函数体内不能含有通过闭包获得的引用
const workScript=`
self.onmessage=({data})=>console.log(data);
`;
//2.生成Blob对象:二进制数据
const workScriptBlob=new Blob([workScript]);
//3.创建对象URL
const workScriptBlobURL=URL.createObjectURL(workScriptBlob);
//4.创建worker
const worker=new Worker(workScriptBlobURL);//
//简写作
const worker=new Worker(URL.createObjectURL(new Blob([`
self.onmessage=({data})=>console.log(data);
`])));
这个newWork文件是在后台加载的,工作线程的初始化完全独立于main.js
工作者线程本身独立于main.js的环境,main.js必须以Worker对象为代理实现与工作者线程的通信。
Worker对象_通信(是脚本源的,即父亲)
是与刚创建的专用工作者线程通信的链接点,与工作者线程的通信都是通过异步消息完成的。
支持下列事件处理程序属性(对应都有两种方式实现):
onerror事件
worker.addEventListener(‘error’,handler)
onmessage事件:监听工作者线程中的消息事件
worker.addEventListener(‘message’,handler)
onmessageerror事件
worker.addEventListener(‘messageerror’,handler)
Worker对象还支持这些方法
postMessage():异步向所创建线程的源窗口发送消息,信息传输涉及调用全局对象上的方法。其实还有很多通信API
terminate():没有清理的机会,立即终止工作者线程(外部
terminate:消息队列清理并锁住
close&