JavaScript是单线程的,但WebWorkers规范允许Web应用程序可以在独立于主线程的后台线程中,创建子线程,运行一个脚本操作,从而可以在独立的线程中执行费时的处理任务,进而不会阻塞主线程(通常是UI线程),Worker线程计算结束后,再把结果返回给主线程,这样就解决了Javascript运行时会干扰用户界面的问题; 例如: worker。js:varnum0;setInterval(function(){postMessage(num);},1000); 浏览器实现WebWorkers的方式有多种,可以使用线程、后台进程或者运行在其他处理器核心上的进程等等;WebWorkers起初是作为HTML5标准的一部分,但后来独立成一个相近的标准;IE9及以下不支持Workers。 应用场景: WebWorkers的实现为Web应用带来了后台计算的能力,它可以将一些耗时的数据处理操作从主线程中剥离,从而极大减轻了因计算量大造成的UI阻塞而出现的界面渲染卡、掉帧的情况,使主线程更加专注于页面渲染和交互,更大程度地利用了终端硬件的性能; 其应用的场景:数学运算、大数据处理、懒加载、文本分析、流媒体数据处理、canvas图形绘制、图像处理等等。 专用Worker和共享Worker: worker有两大类,一个是专用Worker(dedicatedworker),是专门为某个特定的页面服务的;二是共享Worker(sharedworker),这种worker可以在浏览器的多个标签中打开的同一个页面间共享。 专用Worker: 只能由生成它的脚本所使用; 一个Worker包含两部分,一部分是Worker对象,该对象用于创建worker线程;第二部分是DedicatedWorkerGlobalScope,这是一个用来表示新创建的Worker内部的全局对象,也就是Worker线程内部使用的上下文对象; 使用Worker(URL〔,options〕)构造函数实例化Worker对象,该对象执行指定的URL脚本; worker。js线程:console。log(myisworker); 主线程:varworkernewWorker(worker。js);console。log(worker);Worder 参数URL指定的JavaScript文件,包含了将运行独立于主线程的worker线程中运行的代码; 参数URL指定的workerJS文件必须遵守同源策略,如果该URL是一个无效的URL,或者违反同源策略,将抛出SECURITYERR异常;如果此URL的MIME类型不是textjavascript类型,将抛出NetworkError异常;如果URL无法解析,将引发SyntaxError异常; 参数options是可选的配置对象,可用属性如下:type:指定worker类型的字符串,可能的值是classic或module,如果未指定,将使用默认值credentials:用以指定worker凭证的字符串,可能的值是omit(不要求凭证)、sameorigin或include,如果未指定,或者type是classic,将使用默认值name:用于指定Worker的名称,用来区分多个Worker线程,主要用于调试目的; 如主线程:varworkernewWorker(worker。js,{type:classic,credentials:omit,name:myWorker});console。log(worker); 虽然可以在参数中指定options,但该参数的值只能在Worker线程中才能访问;如,Worker线程:postMessage({name:self。name,type:self。type,credential:self。credential}); 主线程:worker。onmessagefunction(event){console。log(event。data);}; worker线程和主线程之间的数据传递,是通过这样的消息机制进行的:双方都使用postMessage()方法发送各自的消息,再使用onmessage事件处理函数来响应消息,且消息被包含在onmessage事件的data属性中; 当页面在Worker对象上调用postMessage()时,数据会以异步方式被传递给worker线程,进而触发worker线程中的message事件; 它们的通信是双向的,反过来也是一样; 使用Worker对象的postMessage(message〔,transferList〕)方法向worker传递消息,参数message为要发送的消息数据,参数transferList是可选的,它是一个Transferable对象的数组,用于转移所有权;如主线程:worker。postMessage(wangwei); Worker线程是通过onmessage和onerror事件来接收数据和处理错误的,如:onmessagefunction(event){console。log(event);MessageEventconsole。log(event。data);}onerrorfunction(event){console。log(event);} 该事件类型是MessageEvent类型; worker线程向主线程传递消息: 与主线程向worker线程传递消息一样,只不过由worker线程通过postMesage()发送消息,主线程通过onmessage和onerror事件接收鼠数据,如,主线程:worker。onmessagefunction(event){console。log(Main:event。data);};worker。onerrorfunction(event){console。log(event);} worker线程:postMessage(我是worker线程); 参数message可以是任何能够被序列化的值,也可以是对象,如:worker。postMessage({type:command,message:start}); 该方法一次只能发送一个值或对象,因此,如果想传递多个值,可以使用数组,如,主线程:worker。postMessage(〔wangwei,18〕); worker线程:onmessagefunction(event){console。log(event。data);} Worker如果不能完成给定的任务时会触发error事件;如:worker。onerrorfunction(event){console。log(event);ErrorEventconsole。log(Error:event。filename(event。lineno):event。message);}; 建议在使用Worker时,始终都要使用onerror事件,因为,不注册onerror事件,Worker会在发生错误的时候,静默失败; 在message事件中,还可以使用postMessage()方法回送数据;如,主线程:varworkernewWorker(worker。js);worker。postMessage(Main向Worker发送的消息);worker。onmessagefunction(event){console。log(Main:event。data);}worker。onerrorfunction(event){console。log(event);} worker。js线程:onmessagefunction(event){console。log(worker:event。data);postMessage(Worker向Main回发的消息);} 示例:数字排序,主线程:vardata〔18,45,11,98,3,6,768〕;worker。postMessage(data);worker。onmessagefunction(event){console。log(event。data);}; worker线程:self。onmessagefunction(event){vardataevent。data。sort(function(a,b){});self。postMessage(data);}; 示例:计算两个数的乘积:stylehtml{backgroundcolor:7D2663;}p{margin:0;}。controls{width:75;margin:10padding:4backgroundcolor:rgba(255,255,255,0。7);border:5opacity:0。3;transition:1}。controls:hover,。controls:focus{opacity:1;}。controlslabel,。controlsp,。controlsinput{fontsize:3}。controlsp{paddingbottom:1}stylelabelfornumber1乘数1:labelinputtypetextidnumber1value0labelfornumber2乘数2:labelinputtypetextidnumber2value0pclassresult结果:0 worker。js:onmessagefunction(e){console。log(Worker:从主线程接收到乘数);varresulte。data〔0〕e。data〔1〕;if(isNaN(result)){postMessage(请输入两个数字);}else{varworkerResult结果:console。log(Worker:已计算出结果,并回显);postMessage(workerResult);}} 终止Worker: worker线程一旦新建成功,就会始终运行,不会被主线程上的活动打断;这样有利于随时响应主线程的通信,但是,这也造成了Worker比较耗费资源的问题,所以不应该过度使用worder,而且一旦使用完毕,就应该终止; 在任何时候,只要调用terminate()方法就可以终止Worker的工作;此时,worker线程中的代码会立即停止执行,后续的所有过程都不会再发生,包括error和message事件也不会再被触发,如:worker。terminate(); 主线程:等待接收消息。。。buttonidbtnStart开始buttonbuttonidbtnClose终止button worker线程:varcount0;self。onmessagefunction(event){self。postMessage(WorkerStart:);setInterval(function(){this。postMessage(我是模拟的数据,不要当真count);},2000);}; 可以根据主线程发来的数据,worker线程可以调用不同的方法,如:主线程:等待接收消息。。。buttonidbtnStart开始buttonbuttonidbtnClose终止button worker线程:varcount0;self。onmessagefunction(event){vardataevent。switch(data。cmd){casestart:self。postMessage(WorkerStart:data。msg);setInterval(function(){this。postMessage(我是模拟的数据,不要当真count);},2000);casestop:self。postMessage(WorkerStop:data。msg);self。close();default:self。postMessage(data。msg);};}; DedicatedWorkerGlobalScope(Worker线程): 关于Worker,最重要的是要知道它所执行的JavaScript代码完全在另一个作用域(上下文)中,即DedicatedWorkerGlobalScope,其代表了专用worker的上下文,与当前主线程页面中的代码不共享作用域,也就是不同于当前的 其继承自WorkerGlobalScope类;该类是DedicatedWorkerGlobalScope、SharedWorkerGlobalScope和ServiceWorkerGlobalScope类的父类; worker线程:console。log(self);DedicatedWorkerGlobalScope 在worker内部中,this、self指向DedicatedWorkerGlobalScope这个上下文,在使用时也可以省略,如:使用thisthis。onmessagefunction(event){console。log(event);}使用selfself。onerrorfunction(event){console。log(event);}省略postMessage(我是worker线程); 为便于处理数据,WebWorkers本身是一个最小化的运行环境: 最小化的navigator对象,包括onLine、appName、appVersion、userAgent和platform属性,在Worker线程中被称为WorkerNavigator对象; 只读的location对象,在Worder线程中是WorkerLocation对象; 还可以使用window对象的部分API,如:setTimout()、setInterval()、clearTimeout()和clearInterval()方法、XMLHttpRequest,也包括WebSockets、IndexedDB等; 显然,WebWorkers的运行环境与页面环境相比,功能是相当有限的;并且在Worker中的代码不能访问DOM,也不能通过任何方式影响页面的外观,只能使用少部分的window的功能; 如果想操作DOM,只能间接地实现,即通过postMessage发送消息给主线程,然后在主线程那里执行DOM操作; 主线程操作DOM:主线程:worker。onmessagefunction(event){varpersonevent。varpdocument。createElement(p);p。innerHTML姓名:person。name 性别:person。sex 年龄:person。document。body。appendChild(p);} worker线程:postMessage({name:wangwei,sex:true,age:18}); 在worker线程内部,调用close()方法也可以停止工作,就像在主线程中调用terminate()方法一样,Worker停止工作后就不会再有事件触发了,如:self。close(); 示例:计算斐波那契数,fibonacci。js:varresults〔〕;functionresultReceiver(event){results。push(parseInt(event。data));if(results。length2)postMessage(results〔0〕results〔1〕);}functionerrorReceiver(event){throwevent。}onmessagefunction(event){varnparseInt(event。data);if(n0n1){postMessage(n);}for(vari1;i2;i){varworkernewWorker(fibonacci。js);worker。onmessageresultRworker。onerrorerrorRworker。postMessage(ni);}}; 主页面: 生成subworker: 如果需要的话,Worker也可以生成多个Worker,这就是所谓的subworker解析URI时会相对于父worker的地址而不是主页面的地址;主线程:varworkernewWorker(worker。js);worker。onmessagefunction(event){console。log(event。data);}; worker。js:varsubWorkernewWorker(subworker。js);subWorker。onmessagefunction(event){event。data。age18;postMessage(event。data);}; subworker。js:postMessage({name:wangwei,message:我是subworker}) subworker的error事件名为 在多核cpu,且要进行并行计算的情况下,使用subworker特别有用; 示例:使用多个subworker并行计算主线程:varworkernewWorker(worker。js);worker。onmessagefunction(event){document。getElementById(result)。textContentevent。}; worker线程:varnumworkers10;varitemsperworker100;varresult0;for(vari0;i){varworkernewWorker(core。js);worker。postMessage(iitemsperworker);worker。postMessage((i1)itemsperworker);worker。onmessagemessageH}functionmessageHandler(event){resultevent。pendingworkers1;if(pendingworkers0)postMessage(result);结束} core。js:onmessagegetSfunctiongetStart(event){startevent。console。log(start:start);onmessagegetE}functiongetEnd(event){endevent。console。log(end:end);work();}functionwork(){console。log(finish:startstart,endend);varresult0;for(i){result1;}postMessage(result);close();} 包含其他脚本(引入脚本和库): 可以调用importScripts(urls)方法,这个方法接收一个或多个指向JavaScript文件的URL,如:importScripts(file1。js,file2。js,file3。js); 每个加载过程都是异步进行的,所有脚本加载并执行之后,importScripts()才会返回,但该方法本身是同步的方案; 执行的时候会按照importScirpts()方法里参数的先后顺序执行; 如果载入脚本的时候抛出了网络错误,或者在执行的时候抛出了错误,那么剩下的脚本就不会载入和运行; 而且,这些脚本是在Worker的全局作用域中执行; 其error事件名为onmessageerror 可以利用importScripts()方法,把一个大的worker线程,拆分成若干个部分,每个部分负责自身的逻辑; 通过importScripts()方法载入的脚本自身还可以调用importScripts()方法载入它需要的文件; 但是,要注意的是,importScripts()方法不会试图去跟踪哪些脚本已经载入了,也不会去防止循环依赖的问题; Worder执行模型: Worker线程从上到下同步运行它们的代码(以及所有导入的脚本),然后进入一个异步阶段,来对事件以及计时器做出响应;如果Worker注册了onmessage事件,只要该事件触发了,那么它将永远不会退出;但是,如果Worker没有监听任何消息,那么,一直到所有任务相关的回调函数都调用以及再也没有挂起的任务(比如下载和计时器)之后,它就会退出;但如果在执行完任务后,又有新任务了,它还要继续执行,不会退出; Worker的数据传输: 结构复制算法:Worker内部的运行机制是,先将消息内容串行化,然后把串行化后的字符串发给Worker,后者再将它还原,如此,传送前和传送后的对象,都是独立的,互不影响,如: 主线程:varperson{name:wangwei,age:18}worker。postMessage(person); worker线程:this。onmessagefunction(event){event。data。console。log(event。data);} 这种方式被称为结构化复制算法;其特点是:对象里不能含有方法,也不能拷贝方法;对象里不能含有symbol,也不能拷贝symbol,键为symbol的属性也会被忽略;自定义对象的类信息会丢失,如:传递一个objnewPerson(),收到的数据将没有Person这个类信息;但如果是一个内置对象,如Number,Boolean这样的对象,则不会丢失!不可枚举的属性(enumerable为false)将会被忽略;属性的可写性配置(writable配置)将丢失; 主线程:varperson{name:wangwei,age:18,抛出异常,不能被clonesmoking:function(){console。log(红南京)},抛出异常,不能被clonesymbol:Symbol(zeronetwork),company:{name:零点程序员,address:北京东城}};person。Object。defineProperty(person,salary,{value:8000,writable:false,configurable:false,enumerable:true如果为false,就不可复制});worker。postMessage(person);worker。onmessagefunction(event){console。log(event。data);}; worker线程:self。onmessagefunction(event){传过来之后,就可以修改了,因为指定的writable配置被丢弃了event。data。salary20000;console。log(event。data);}; 主线程与worker线程之间也可以交换二进制数据,比如File、Blob、ArrayBuffer等类型,如:主线程:varuInt8ArraynewUint8Array(newArrayBuffer(10));for(vari0;iuInt8Array。i){uInt8Array〔i〕i2;〔0,2,4,6,8,。。。〕}worker。postMessage(uInt8Array); worker线程:self。onmessagefunction(event){varuInt8Arrayevent。postMessage(worker。js:uInt8Array。toString());postMessage(uInt8Array。byteLength:uInt8Array。byteLength);for(vari0;iuInt8Array。i){uInt8Array〔i〕0;}postMessage(update:uInt8Array。toString());}; 传值(拷贝)方式在发送二进制数据,会造成性能问题; 通过转移所有权(可转移对象)来传递数据(可转移也称为可转让): 为了解决这个问题,JavaScript允许主线程把二进制数据直接转移给子线程,但是一旦转移,主线程就无法再使用这些二进制数据了,这是为了防止出现多个线程同时修改数据;这种转移数据的方法,叫做TransferableObjects可转移对象;可转移对象从一个上下文转移到另一个上下文而不会经过任何拷贝操作,这意味着当传递大数据时会获得极大的性能提升;,对于影像处理、声音处理、3D运算等就非常方便了,不会产生性能负担; 使用postMessage(message〔,transfer〕)方法的第二个参数transfer,其是Transferable对象的数组,用于传递所有权,可转移对象可以是ArrayBuffer,MessagePort或ImageBitmap等实例对象,如:主线程:varworkernewWorker(worker。js);varuInt8ArraynewUint8Array(1024102432);32MBfor(vari0;iuInt8Array。i){uInt8Array〔i〕i;}console。log(uInt8Array。length);worker。postMessage(uInt8Array。buffer,〔uInt8Array。buffer〕);console。log(uInt8Array。length);worker。onmessagefunction(event){console。log(event。data);}; worker线程:self。onmessagefunction(event){varbufferevent。console。log(buffer。byteLength);postMessage(buffer,〔buffer〕);console。log(buffer。byteLength);}; 可转移对象的类型: ArrayBuffer、MessagePort、ReadableStream、WritableStream、TransformStream、AudioData、ImageBitmap、VideoFrame、OffscreenCanvas、RTCDataChannel。 嵌入式(内联)worker: 可以把worker代码嵌入到主文档中,主要是借助codepre 由于嵌入式worker不需要从远程资源载入,所以这使得worker初始化更快;或者不使用codepre 在实例化Worker时,还可以直接把worker字符串作为参数,如:varworkernewWorker(data:charsetUTF8,onmessagefunction(event){postMessage(worker:收到的数据event。data);});worker。postMessage(零点网络);worker。onmessagefunction(event){console。log(Main:event。data);}; Worker与Ajax: 表面上看,Ajax和WebWorker都是异步执行的,但两者还是有很大的区别;Ajax是异步的,但是它的运行还是单线程的; Worker是多线程的,其子线程与主线程是分开的,因此不管子线程运行是否阻塞,还是主线程阻塞,它们之间不会相互影响; 有些情况下,Worker还需要与服务器端进行交互,比如页面可能需要处理来自数据库中的信息,此时就需要使用Ajax技术与服务器交互;但XMLHttpRequest的responseXML和channel这两个属性的值为null,如:varworkernewWorker(worker。js);varurlexample。worker。postMessage(url);worker。onmessagefunction(event){console。log(event。data);} worker。js:varxhrnewXMLHttpRequest();xhr。onloadfunction(event){self。postMessage(xhr。response);};onmessagefunction(event){varurlevent。xhr。open(GET,url);xhr。send(null);} 完善,主页面:varworkernewWorker(worker。js);worker。onmessagefunction(event){console。log(event。data);};vardata{method:GET,url:example。json}worker。postMessage(data);vardata{method:POST,url:example。php,params:namewangweisex男age18};worker。postMessage(data); worker。js:varxhrnewXMLHttpRequest();xhr。onloadfunction(event){self。postMessage(xhr。response);};onmessagefunction(event){vardataevent。xhr。open(data。method,data。url);varparamsdata。if(params)xhr。setRequestHeader(ContentType,applicationxwwwformurlencoded);xhr。send(params);} 在Worker中发起同步XMLHttpR主线程:inputtypebuttononclickgetContent()value获取内容 Worker线程:onmessagefunction(e){varurlse。varcontents〔〕;输出for(vari0;iurls。i){varurlurls〔i〕;varxhrnewXMLHttpRequest();xhr。open(GET,url,false);同步XHRxhr。send();if(xhr。status!200){throwError(xhr。statusxhr。statusText:url);}contents。push(xhr。responseText);}postMessage(contents);} 示例:在嵌入式worker中使用Ajax:varasyncEval(function(){varaListeners〔〕;varworkernewWorker(data:charsetUTF8,onmessagefunction(e){postMessage({id:e。data。id,evaluated:eval(e。data。code)});})worker。onmessagefunction(e){if(aListeners〔e。data。id〕)aListeners〔e。data。id〕(e。data。evaluated);deleteaListeners〔e。data。id〕;};returnfunction(sCode,fListener){aListeners。push(fListenernull);worker。postMessage({id:aListeners。length1,code:sCode});};})();应用asyncEval(32,function(sMessage){alert(32sMessage);});asyncEval(大师哥,function(sHTML){document。body。appendChild(document。createTextNode(sHTML));});asyncEval((function(){varxhrnewXMLHttpRequest();xhr。open(GET,http:localhostdemo。php,false);xhr。send(null);returnxhr。responseT})(),function(message){console。log(message);}); 示例:在一个Worker线程中执行长时间计算;主线程:imgsrcc2021imgdataimg。jpgdatasrcimgq01。71396。combkkh989001245fd0188b。jpgonclicksmear(this) 线程smearWorker。js:onmessagefunction(e){postMessage(smear(e。data));};functionsmear(pixels){vardatapixels。data,widthpixels。width,heightpixels。varn10,mn1;for(varrow0;row){varirowwidth44;for(varcol1;col,i4){data〔i〕(data〔i〕data〔i4〕m)n;data〔i1〕(data〔i1〕data〔i3〕m)n;data〔i2〕(data〔i2〕data〔i2〕m)n;data〔i3〕(data〔i3〕data〔i1〕m)n;}}} 共享Worker(SharedWorker): 一个SharedWorker可以被多个脚本使用,也就是可以被不同的窗口的多个脚本运行,例如window、Iframes或worker等,可以实现多页面之间的数据共享与通信;共享worker比专用worker稍微复杂一点,它的脚本必须通过活动端口进行通讯;共享worker的上下文是SharedWorkerGlobalScope对象,也是继承自WorkerGlobalScope类; 创建SharedWorker: 使用SharedWorker(URL〔,nameoptions〕)构造函数实例化一个SharedWorker对象,参数URL指定了要执行URL脚本,所执行的脚本必须遵守同源策略;参数name表示worker范围的标识名称,主要用于调试;参数options为可选,与Worker()参数中的options相同;如:varsharedWorkernewSharedWorker(worker。js);console。log(sharedWorker);SharedWorker port属性:只读,返回一个MessagePort对象,该对象可以用来进行通信,并能对共享worker进行控制;console。log(sharedWorker。port);MessagePort MessagePort类型:MessagePort是属于ChannelMessagingAPI当中的一个接口; MessagePort事件: onmessage:当该端口收到了一条消息时触发; onmessageerror:当端口收到了一条无法被反序列化的消息时触发;sharedWorker。port。onmessagefunction(event){console。log(event);MessageEventconsole。log(event。data);};sharedWorker。port。onmessageerrorfunction(event){console。log(event);}; onmessage事件中的事件对象类型是MessageEvent类型; MessagePort方法: 启动端口时,可以使用postMessage()方法:从当前端口发送一条消息:sharedWorker。port。postMessage(Main:发送的数据);sharedWorker。port。onmessagefunction(event){console。log(Main:event。data);}; start():在传递消息前,端口连接必须被显式打开,即使用start()方法开始发送该端口中的消息;sharedWorker。port。start(); 当使用onmessage注册事件,默认port就已经打开了;如果使用addEventListener注册message事件,就必须使用start()方法打开如:myWorker。port。start();myWorker。port。addEventListener(message,function(event){console。log(event。data);}); 因此,推荐使用DOM1级事件注册机制; 在使用start()方法打开端口连接时,如果主页面和SharedWorker需要双向通信,那么它们都需要调用start()方法,如:。。。port。start();worker线程中的调用,假设port变量代表一个端口 每个新的SharedWorker连接都触发一个onconnect事件; 在Worker中使用onconnect处理程序连接到相关端口;可以用SharedWorker内部的onconnect事件的event对象的ports属性中获取到与该SharedWorker相连接的端口; 使用ports〔0〕获取当前使用的port,即MessagePort对象,如:onconnectfunction(event){varportevent。ports〔0〕;port。postMessage(portsevent。ports);ports〔objectMessagePort〕port。postMessage(event。ports。length);1}; 发送数据使用port对象的postMessage()方法,如:onconnectfunction(event){varportevent。ports〔0〕;port。postMessage(workerstart);}; 使用port对象start()方法来启动端口或直接使用onmessage处理程序处理来自主线程的消息,并可以向主页面传递消息,如:onconnectfunction(e){varporte。ports〔0〕;port。addEventListener(message,function(e){port。postMessage(接收到数据为:e。data);});port。start();} 但还是建议DOM1级事件注册,如:onconnectfunction(event){varportevent。ports〔0〕;port。onmessagefunction(event){port。postMessage(warker:接收到的消息再返回:event。data);}}; SharedWorkerGlobalScope: 在共享worker内部,全局作用域是SharedWorkerGlobalScope的一个实例,它继承自WorkerGlobalScope,因此包括它的所有属性和方法;和专用worker一样,共享worker可以通过self访问全局作用域; 共享数据:index。html和index1。html,两个页面结构一样,如:pinputtypebuttonvalue点击一下idbtnpspan点了spanclasstimespan下span worker线程:vara1;全局变量onconnectfunction(e){varporte。ports〔0〕;port。onmessagefunction(){port。postMessage(a)}} 示例:点赞,主页面:buttonidlikeBtn点赞buttonp一共收获了spanidlikedCount0span个赞 sharedworker。js:varcount666;self。onconnectfunction(event){varportevent。ports〔0〕;port。postMessage(count);port。onmessagefunction(e){port。postMessage(count);}} 在SharedWorker中,可以根据获取到不同的值,发送不同的数据,如:主页面:varsharedWorkernewSharedWorker(sharedworker。js);sharedWorker。port。onmessagefunction(event){console。log(来自worker的消息:event。data);};sharedWorker。port。postMessage(get);A页面用sharedWorker。port。postMessage(发送新数据给worker);B页面用 worker。js:vardataworker中原始数据;self。onconnectfunction(event){varportevent。ports〔0〕;port。onmessagefunction(e){if(e。dataget){port。postMessage(data);}else{datae。port。postMessage(worker:data);后续处理}}} DedicatedWorker与SharedWorker的本质区别: 专用Worker只属于某个页面,不会和其他页面的Render进程(浏览器内核进程)共享,所以在Render进程中创建一个新的线程来运行Worker中的JavaScript程序; SharedWorker是浏览器所有页面共享的,不能采用与Worker同样的方式实现,因为它不隶属于某个Render进程,可以为多个Render进程共享使用,所以浏览器为SharedWorker单独创建一个进程来运行JavaScript程序,在浏览器中使用相同名字的SharedWorker的多个脚本,只存在一个SharedWorker进程,不管它被创建多少次; 共享worker身份由解析脚本URL、worker名和文档源确定;newSharedWorker(。sharedWorker。js);newSharedWorker(。sharedWorker。js);newSharedWorker(。sharedWorker。js);newSharedWorker(。sharedWorker。js);newSharedWorker(sharedWorker。js);newSharedWorker(http:localhostsharedWorker。js); 即使加载的是同一个脚本,但解析出来的URL不一致,也不能共享,如:newSharedWorker(。sharedWorker。js);newSharedWorker(。sharedWorker。js?); 因为可选的worker名是共享worker身份的一部分,使用不同的worker名会让浏览器强制创建多个共享worker,尽管它们的源和脚本URL相同;newSharedWorker(。sharedWorker。js,{name:one});newSharedWorker(。sharedWorker。js,{name:one});newSharedWorker(。sharedWorker。js,{name:two}); 共享worker可以在标签、窗口、iframe或其他运行在同一个源的worker之间共享;如:index。html SharedWorker也可以使用importScripts()方法;但不能使用共享subworker,在某些浏览器中,可以使用专用subworker,例如Firefox可以使用,如:主页面:varsharedWorkernewSharedWorker(worker。js);sharedWorker。port。onmessagefunction(event){console。log(Main:event。data);}; worker。js:importScripts(w1。js,w2。js);onconnectfunction(e){varporte。ports〔0〕;port。postMessage(返给主页面,我用到了subworker);varsubworkernewWorker(subworker。js);subworker。onmessagefunction(event){console。log(worker:event。data);port。postMessage(worker转发给main:event。data);};subworker。postMessage(worker发送给subworker);} subworker。js:onmessagefunction(event){console。log(subworker:event。data);postMessage(subworker发送给worker);}; 在一个脚本中,可以同时使用专用Worker和共享W如:主页面index。html:varworkernewWorker(worker。js);varsharedWorkernewSharedWorker(sharedworker。js);worker。onmessagefunction(event){console。log(event。data);再扔给sharedWorkersharedWorker。port。postMessage(event。data);sharedWorker。port。onmessagefunction(e){console。log(e。data);};};给专用worker一个对象worker。postMessage({name:wangwei,age:18}); worker线程:onmessagefunction(event){varpersonevent。进行必要的处理,如:person。person。company{name:零点程序员,address:北京东城}加工一下,再送回去postMessage(person);}; sharedworker。js:onconnectfunction(event){varportevent。ports〔0〕;port。onmessagefunction(e){if(!obj)obje。port。postMessage(obj);}} close()方法:断开端口连接,它将不再是激活状态;如:主页面:worker。port。close()仅仅关闭连接SharedWorker内部:port。close()仅仅关闭连接 不能人为终止共享SharedWorker,在MessagPort对象上调用close()也不会导致共享worker的终止,它仅仅是断开连接,并不会销毁SharedWorker,即使只有一个页面和共享worker连接,其还会持续运行; 另外SharedWorker没有类似terminate()的方法;其运行的状态与相应的MessagePort或MessageChannel的状态无关;一旦建立并连接SharedWorker,连接的管理就由浏览器负责,并且所建立的连接将持续在页面的生命周期中持续,只有全部页面销毁,没有连接的时候,且该SharedWorker内部没有运行中的任务,浏览器才终止该SharedW Worker(专用和共享)内部中的事件,主线程是没法监听到的,反之亦然;Worker中的异常,主线程也是无法感知的,反之亦然;二者唯一的交互方式就是postMessage和监听message事件;如:主页面:inputtypebuttonvalue发送消息idbtnSendinputtypebuttonvalue关闭连接idbtnClose sharedworker:varconnectList〔〕;onconnectfunction(event){varportevent。ports〔0〕;port。onmessagefunction(e){port。postMessage(收到消息:e。data);if(connectList。indexOf(port)1)connectList。push(port);connectList。forEach(function(v,i){v。postMessage(现在有连接:connectList。length);})}} 清除SharedWorker: 在页面关闭后,workerPool中的port并不会自动清除,会造成内存的占用;通过监听页面关闭或页面生命周期的window。onbeforeunload,通知SharedWorker关闭此连接;如:主页面:varbtnClosedocument。getElementById(btnClose);btnClose。onclickfunction(){sharedWorker。port。postMessage({action:close});sharedWorker。port。close();console。log(sharedWorker已关闭);} sharedworker。jsvarconnectList〔〕;onconnectfunction(event){varportevent。ports〔0〕;port。onmessagefunction(e){if(connectList。indexOf(port)1)connectList。push(port);if(e。data。actionclose){port。close();connectListconnectList。filter(function(item){returnitem!})}connectList。forEach(function(v,i){v。postMessage(现在有连接:connectList。length);});}} 当关闭窗口时,包括刷新重新载入,也需要通知SharedWorker,如:主页面添加:window。onbeforeunloadfunction(){sharedWorker。port。postMessage({action:close})} 错误的处理: SharedWorker内部的错误,无论是其内部还是主页面都无法获取,如:主页面:varsharedWorkernewSharedWorker(sharedworker。js);console。log(sharedWorker);console。log(sharedWorker。port);sharedWorker。port。postMessage(随便发点啥);sharedWorker。port。onmessagefunction(event){console。log(event。data);};sharedWorker。onerrorfunction(event){console。log(event);};sharedWorker。port。onerrorfunction(event){console。log(event);};sharedWorker。port。onmessageerrorfunction(event){console。log(event);}; sharedworker。js:onconnectfunction(event){varportevent。ports〔0〕;console。log(port);port。onmessagefunction(e){thrownewError(有错了);};port。onmessageerrorfunction(event){console。log(event);}} sharedWorker。onerror,在共享工作进程中发生错误时触发; port。onmessageerror事件,当MessagePort对象接收到无法反序列化的消息时触发; 示例:计算两个数的积varfirstdocument。querySelector(number1);varseconddocument。querySelector(number2);varresultdocument。querySelector(。result);if(!!window。SharedWorker){varmyWorkernewSharedWorker(sharedworker。js);first。onchangefunction(){myWorker。port。postMessage(〔first。value,second。value〕);console。log(向worker中发送第一个乘数);}second。onchangefunction(){myWorker。port。postMessage(〔first。value,second。value〕);console。log(向worker中发送第二个乘数);}myWorker。port。onmessagefunction(event){result。textContentevent。console。log(从worker接收到计算结果:event。data);}}else{result。textContent浏览器不支持} sharedworker2。html:labelfornumber3平方数:labelinputtypetextidnumber3value0p上一次结果:spanclassresultspansharedworker。js:varresult0;onconnectfunction(event){varportevent。ports〔0〕;port。postMessage(result);port。onmessagefunction(e){resulte。data〔0〕e。data〔1〕;port。postMessage(result);}} 在SharedWorker中,利用ports属性向所有连接页面分发消息,如:pinputtypetextclassinputbuttonidbtn提交buttonpworker2。html sharedworker。js:varconnectList〔〕;vartextList〔〕;onconnectfunction(event){varportevent。ports〔0〕;port。onmessagefunction(e){if(connectList。indexOf(port)1)connectList。push(port);switch(e。data。status){case0:postMsg(function(item){if(item!port)item。postMessage(有新用户加入);elseitem。postMessage(我是新用户);});default:textList。push(e。data。value);postMsg(textList);}}}分发消息functionpostMsg(callback){varcallback(typeofcallbackfunction)?callback:function(item){item。postMessage(callback);}connectList。forEach(callback);} 示例:页面之间登录状态通知:主页面:style{margin:0;padding:0}。container{background:rgb(38,7,95)url(imagesbg。png)backgroundsize:height:1000}。tips{display:height:80width:100;textalign:lineheight:80backgroundcolor:position:top:0;}stylelogininfo。。。 login。html登录页:inputtypebuttonvalue登录idbtnLogininputtypebuttonvalue退出idbtnLogout sharedworker。js:varisLvarconnectList〔〕;vartipsTextArray〔Yousignedinanothertaborwindow。Reloadtorefreshyoursession。,Yousignedoutinanothertaborwindow。Reloadtorefreshyoursession。〕;onconnectfunction(event){varportevent。ports〔0〕;if(connectList。indexOf(port)1)connectList。push(port);port。onmessagefunction(e){isLogine。data。isLtipsTextisLogin?tipsTextArray〔0〕:tipsTextArray〔1〕;connectList。forEach(function(item){item。postMessage({isLogin:isLogin,tipsText:tipsText});})};}