范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

第77节WebWorkers零点程序员王唯

  JavaScript是单线程的,但Web Workers规范允许Web应用程序可以在独立于主线程的后台线程中,创建子线程,运行一个脚本操作,从而可以在独立的线程中执行费时的处理任务,进而不会阻塞主线程(通常是UI线程),Worker线程计算结束后,再把结果返回给主线程,这样就解决了Javascript运行时会干扰用户界面的问题;
  例如:
  worker.js:var num = 0; setInterval(function(){ postMessage(num++); },1000);
  浏览器实现Web Workers的方式有多种,可以使用线程、后台进程或者运行在其他处理器核心上的进程等等;Web Workers起初是作为HTML5标准的一部分,但后来独立成一个相近的标准;IE9及以下不支持Workers。
  应用场景:
  Web Workers的实现为Web应用带来了后台计算的能力,它可以将一些耗时的数据处理操作从主线程中剥离,从而极大减轻了因计算量大造成的UI阻塞而出现的界面渲染卡、掉帧的情况,使主线程更加专注于页面渲染和交互,更大程度地利用了终端硬件的性能;
  其应用的场景:数学运算、大数据处理、懒加载、文本分析、流媒体数据处理、canvas图形绘制、图像处理等等。
  专用Worker和共享Worker:
  worker有两大类,一个是专用Worker(dedicated worker),是专门为某个特定的页面服务的;二是共享Worker(shared worker),这种worker可以在浏览器的多个标签中打开的同一个页面间共享。
  专用Worker:
  只能由生成它的脚本所使用;
  一个Worker包含两部分,一部分是Worker对象,该对象用于创建worker线程;第二部分是DedicatedWorkerGlobalScope,这是一个用来表示新创建的Worker内部的全局对象,也就是Worker线程内部使用的上下文对象;
  使用Worker(URL[, options])构造函数实例化Worker对象,该对象执行指定的URL脚本;
  worker.js线程:console.log("my is worker");
  主线程:var worker = new Worker("worker.js"); console.log(worker); // Worder
  参数URL指定的JavaScript文件,包含了将运行独立于主线程的worker线程中运行的代码;
  参数URL指定的worker JS文件必须遵守同源策略,如果该URL是一个无效的URL,或者违反同源策略,将抛出SECURITY_ERR异常;如果此URL的MIME类型不是text/javascript类型,将抛出NetworkError异常;如果URL无法解析,将引发SyntaxError异常;
  参数options是可选的配置对象,可用属性如下:type:指定worker类型的字符串,可能的值是classic或module,如果未指定,将使用默认值classic;credentials:用以指定worker凭证的字符串,可能的值是omit(不要求凭证)、same-origin或include,如果未指定,或者type是classic,将使用默认值omit;name:用于指定Worker的名称,用来区分多个Worker线程,主要用于调试目的;
  如主线程:var worker = new Worker("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.onmessage = function(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事件来接收数据和处理错误的,如:onmessage = function(event){ console.log(event); // MessageEvent console.log(event.data); } onerror = function(event){ console.log(event); }
  该事件类型是MessageEvent类型;
  worker线程向主线程传递消息:
  与主线程向worker线程传递消息一样,只不过由worker线程通过postMesage()发送消息,主线程通过onmessage和onerror事件接收鼠数据,如,主线程:worker.onmessage = function(event){ console.log("Main:" + event.data); }; worker.onerror = function(event){ console.log(event); }
  worker线程:postMessage("我是worker线程");
  参数message可以是任何能够被序列化的值,也可以是对象,如:worker.postMessage({ type: "command", message: "start" });
  该方法一次只能发送一个值或对象,因此,如果想传递多个值,可以使用数组,如,主线程:worker.postMessage(["wangwei",18]);
  worker线程:onmessage = function (event) { console.log(event.data); }
  Worker如果不能完成给定的任务时会触发error事件;如:worker.onerror = function(event){ console.log(event); // // ErrorEvent console.log("Error: " + event.filename + " (" + event.lineno + "): " + event.message); };
  建议在使用Worker时,始终都要使用onerror事件,因为,不注册onerror事件,Worker会在发生错误的时候,静默失败;
  在message事件中,还可以使用postMessage()方法回送数据;如,主线程:var worker = new Worker("worker.js"); worker.postMessage("Main向Worker发送的消息"); worker.onmessage = function(event){ console.log("Main: " + event.data); } worker.onerror = function(event){ console.log(event); }
  worker.js线程:onmessage = function(event){ console.log("worker: " + event.data); postMessage("Worker向Main回发的消息"); }
  示例:数字排序,主线程:var data = [18,45,11,98,3,6,768]; worker.postMessage(data); worker.onmessage = function(event){ console.log(event.data); };
  worker线程:self.onmessage = function(event){ var data = event.data; data.sort(function(a,b){ return a - b; }); self.postMessage(data); };
  示例:计算两个数的乘积:          

结果: 0   worker.js:onmessage = function(e) { console.log("Worker: 从主线程接收到乘数"); var result = e.data[0] * e.data[1]; if(isNaN(result)) { postMessage("请输入两个数字"); }else{ var workerResult = "结果: " + result; console.log("Worker: 已计算出结果,并回显"); postMessage(workerResult); } }   终止Worker:   worker线程一旦新建成功,就会始终运行,不会被主线程上的活动打断;这样有利于随时响应主线程的通信,但是,这也造成了Worker比较耗费资源的问题,所以不应该过度使用worder,而且一旦使用完毕,就应该终止;   在任何时候,只要调用terminate()方法就可以终止Worker的工作;此时,worker线程中的代码会立即停止执行,后续的所有过程都不会再发生,包括error和message事件也不会再被触发,如:worker.terminate();   主线程:等待接收消息...   worker线程:var count = 0; self.onmessage = function (event) { self.postMessage("Worker Start: "); setInterval(function(){ this.postMessage("我是模拟的数据,不要当真" + count++); },2000); };   可以根据主线程发来的数据,worker线程可以调用不同的方法,如:主线程:等待接收消息...   worker线程:var count = 0; self.onmessage = function (event) { var data = event.data; switch (data.cmd) { case "start": self.postMessage("Worker Start: " + data.msg); setInterval(function(){ this.postMessage("我是模拟的数据,不要当真" + count++); },2000); break; case "stop": self.postMessage("Worker Stop: " + data.msg); self.close(); break; default: self.postMessage(data.msg); }; };   DedicatedWorkerGlobalScope(Worker线程):   关于Worker,最重要的是要知道它所执行的JavaScript代码完全在另一个作用域(上下文)中,即DedicatedWorkerGlobalScope,其代表了专用worker的上下文,与当前主线程页面中的代码不共享作用域,也就是不同于当前的window;   其继承自WorkerGlobalScope类;该类是DedicatedWorkerGlobalScope、SharedWorkerGlobalScope和ServiceWorkerGlobalScope类的父类;   worker线程:console.log(self); // DedicatedWorkerGlobalScope   在worker内部中,this、self指向DedicatedWorkerGlobalScope这个上下文,在使用时也可以省略,如:// 使用this this.onmessage = function(event){ console.log(event); } // 使用self self.onerror = function(event){ console.log(event); } // 省略 postMessage("我是worker线程");   为便于处理数据,Web Workers本身是一个最小化的运行环境:   最小化的navigator对象,包括onLine、appName、appVersion、userAgent和platform属性,在Worker线程中被称为WorkerNavigator对象;   只读的location对象,在Worder线程中是WorkerLocation对象;   还可以使用window对象的部分API ,如:setTimout()、setInterval()、clearTimeout()和clearInterval()方法、XMLHttpRequest,也包括WebSockets、IndexedDB等;   显然,Web Workers的运行环境与页面环境相比,功能是相当有限的;并且在Worker中的代码不能访问DOM,也不能通过任何方式影响页面的外观,只能使用少部分的window的功能;   如果想操作DOM,只能间接地实现,即通过postMessage发送消息给主线程,然后在主线程那里执行DOM操作;   主线程操作DOM:主线程:worker.onmessage = function(event){ var person = event.data; var p = document.createElement("p"); p.innerHTML = "姓名:" + person.name + "   " + "性别:" + person.sex + "   " + "年龄:" + person.age; document.body.appendChild(p); }   worker线程:postMessage({ name: "wangwei", sex: true, age: 18 });   在worker线程内部,调用close()方法也可以停止工作,就像在主线程中调用terminate()方法一样,Worker停止工作后就不会再有事件触发了,如:self.close();   示例:计算斐波那契数,fibonacci.js:var results = []; function resultReceiver(event){ results.push(parseInt(event.data)); if(results.length == 2) postMessage(results[0] + results[1]); } function errorReceiver(event){ throw event.data; } onmessage = function(event){ var n = parseInt(event.data); if(n == 0 || n == 1){ postMessage(n); return; } for(var i=1; i<=2; i++){ var worker = new Worker("fibonacci.js"); worker.onmessage = resultReceiver; worker.onerror = errorReceiver; worker.postMessage(n - i); } };   主页面:   生成subworker:   如果需要的话,Worker也可以生成多个Worker,这就是所谓的subworker; subworker解析URI时会相对于父worker的地址而不是主页面的地址;主线程:var worker = new Worker("worker.js"); worker.onmessage = function(event){ console.log(event.data); };   worker.js:var subWorker = new Worker("subworker.js"); subWorker.onmessage = function(event){ event.data.age = 18; postMessage(event.data); };   subworker.js:postMessage({ name: "wangwei", message: "我是subworker" })   subworker的error事件名为onmessageerror;   在多核cpu,且要进行并行计算的情况下,使用subworker特别有用;   示例:使用多个subworker并行计算主线程:var worker = new Worker("worker.js"); worker.onmessage = function (event) { document.getElementById("result").textContent = event.data; };   worker线程:var num_workers = 10; var items_per_worker = 100; var result = 0; var pending_workers = num_workers; for(var i = 0; i < num_workers; i++) { var worker = new Worker("core.js"); worker.postMessage(i * items_per_worker); worker.postMessage((i + 1) * items_per_worker); worker.onmessage = messageHandler; } function messageHandler(event){ result += event.data; pending_workers -= 1; if(pending_workers <= 0) postMessage(result); // 结束 }   core.js:var start; onmessage = getStart; function getStart(event) { start = event.data; console.log("start: " + start); onmessage = getEnd; } var end; function getEnd(event) { end = event.data; console.log("end: " + end); onmessage = null; work(); } function work() { console.log("finish: start=" + start + ", end=" + end); var result = 0; for (var i = start; i < end; i++) { result += 1; } 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,后者再将它还原,如此,传送前和传送后的对象,都是独立的,互不影响,如:   主线程:var person = { name: "wangwei", age: 18 } worker.postMessage(person);   worker线程:this.onmessage = function(event){ event.data.name = "jingjing"; console.log(event.data); }   这种方式被称为结构化复制算法;其特点是:对象里不能含有方法,也不能拷贝方法;对象里不能含有symbol,也不能拷贝symbol,键为symbol的属性也会被忽略;自定义对象的类信息会丢失,如:传递一个obj=new Person(),收到的数据将没有Person这个类信息;但如果是一个内置对象,如Number,Boolean这样的对象,则不会丢失!不可枚举的属性(enumerable为false)将会被忽略;属性的可写性配置(writable配置)将丢失;   主线程:var person = { name: "wangwei", age: 18, // 抛出异常,不能被clone // smoking: function(){console.log("红南京")}, // 抛出异常,不能被clone // symbol: Symbol("zeronetwork"), company: { name: "零点程序员", address: "北京东城" } }; person.sex = true; Object.defineProperty(person, "salary", { value: 8000, writable: false, configurable: false, enumerable: true // 如果为false,就不可复制 }); worker.postMessage(person); worker.onmessage = function (event) { console.log(event.data); };   worker线程:self.onmessage = function(event) { // 传过来之后,就可以修改了,因为指定的writable配置被丢弃了 event.data.salary = 20000; console.log(event.data); };   主线程与worker线程之间也可以交换二进制数据,比如 File、Blob、ArrayBuffer等类型,如:主线程:var uInt8Array = new Uint8Array(new ArrayBuffer(10)); for (var i = 0; i < uInt8Array.length; ++i) { uInt8Array[i] = i * 2; // [0, 2, 4, 6, 8,...] } worker.postMessage(uInt8Array);   worker线程:self.onmessage = function(event) { var uInt8Array = event.data; postMessage("worker.js: " + uInt8Array.toString()); postMessage("uInt8Array.byteLength: " + uInt8Array.byteLength); for (var i = 0; i < uInt8Array.length; ++i) { uInt8Array[i] = 0; } postMessage("update: " + uInt8Array.toString()); };   传值(拷贝)方式在发送二进制数据,会造成性能问题;   通过转移所有权(可转移对象)来传递数据(可转移也称为可转让):   为了解决这个问题,JavaScript 允许主线程把二进制数据直接转移给子线程,但是一旦转移,主线程就无法再使用这些二进制数据了,这是为了防止出现多个线程同时修改数据;这种转移数据的方法,叫做Transferable Objects可转移对象;可转移对象从一个上下文转移到另一个上下文而不会经过任何拷贝操作,这意味着当传递大数据时会获得极大的性能提升;,对于影像处理、声音处理、3D 运算等就非常方便了,不会产生性能负担;   使用postMessage(message[, transfer])方法的第二个参数transfer,其是Transferable对象的数组,用于传递所有权,可转移对象可以是ArrayBuffer,MessagePort或ImageBitmap等实例对象,如: 主线程:var worker = new Worker("worker.js"); var uInt8Array = new Uint8Array(1024*1024*32); // 32MB for (var i = 0; i < uInt8Array .length; ++i) { uInt8Array[i] = i; } console.log(uInt8Array.length); worker.postMessage(uInt8Array.buffer, [uInt8Array.buffer]); console.log(uInt8Array.length); worker.onmessage = function (event) { console.log(event.data); };   worker线程:self.onmessage = function(event) { var buffer = event.data; console.log(buffer.byteLength); postMessage(buffer, [buffer]); console.log(buffer.byteLength); };   可转移对象的类型:   ArrayBuffer、MessagePort、ReadableStream、WritableStream、TransformStream、AudioData、ImageBitmap、VideoFrame、OffscreenCanvas、RTCDataChannel。   嵌入式(内联) worker:   可以把worker代码嵌入到主文档中,主要是借助

  由于嵌入式worker不需要从远程资源载入,所以这使得worker初始化更快;或者不使用   在实例化Worker时,还可以直接把worker字符串作为参数,如:var worker = new Worker("data:text/javascript;charset=UTF-8,onmessage = function(event){postMessage("worker: 收到的数据 " + event.data);}"); worker.postMessage("零点网络"); worker.onmessage = function(event){ console.log("Main: " + event.data); };   Worker与Ajax:   表面上看,Ajax和Web Worker 都是异步执行的,但两者还是有很大的区别;Ajax是异步的,但是它的运行还是单线程的;   Worker是多线程的,其子线程与主线程是分开的,因此不管子线程运行是否阻塞,还是主线程阻塞,它们之间不会相互影响;   有些情况下,Worker还需要与服务器端进行交互,比如页面可能需要处理来自数据库中的信息,此时就需要使用Ajax技术与服务器交互;但XMLHttpRequest的responseXML和channel这两个属性的值为null,如:var worker = new Worker("worker.js"); var url = "example.json"; worker.postMessage(url); worker.onmessage = function(event){ console.log(event.data); }   worker.js:var xhr = new XMLHttpRequest(); xhr.onload = function(event){ self.postMessage(xhr.response); }; onmessage = function(event){ var url = event.data; xhr.open("GET", url); xhr.send(null); }   完善,主页面:var worker = new Worker("worker.js"); worker.onmessage = function(event){ console.log(event.data); }; // var data = { // method: "GET", // url: "example.json" // } // worker.postMessage(data); var data = { method: "POST", url: "example.php", params: "name=wangwei&sex=男&age=18" }; worker.postMessage(data);   worker.js:var xhr = new XMLHttpRequest(); xhr.onload = function(event){ self.postMessage(xhr.response); }; onmessage = function(event){ var data = event.data; xhr.open(data.method, data.url); var params = data.params || null; if(params) xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); xhr.send(params); }   在Worker中发起同步XMLHttpRequest;主线程:   Worker线程:onmessage = function(e){ var urls = e.data; var contents = []; // 输出 for(var i=0; i   线程smearWorker.js:onmessage = function(e){ postMessage(smear(e.data)); }; function smear(pixels){ var data = pixels.data, width = pixels.width, height = pixels.height; var n = 10, m = n - 1; for(var row = 0; row < height; row++){ var i = row*width*4 + 4; for(var col=1; col

点了 -   worker线程:var a = 1; // 全局变量 onconnect = function (e) { var port = e.ports[0]; port.onmessage = function () { port.postMessage(a++) } }   示例:点赞,主页面:

一共收获了0个赞   sharedworker.js:var count = 666; self.onconnect = function(event){ var port = event.ports[0]; port.postMessage(count); port.onmessage = function(e){ port.postMessage(++count); } }   在SharedWorker中,可以根据获取到不同的值,发送不同的数据,如:主页面:var sharedWorker = new SharedWorker("sharedworker.js"); sharedWorker.port.onmessage = function(event){ console.log("来自worker的消息:" + event.data); }; sharedWorker.port.postMessage("get"); // A页面用 sharedWorker.port.postMessage("发送新数据给worker"); // B页面用   worker.js:var data = "worker中原始数据"; self.onconnect = function(event){ var port = event.ports[0]; port.onmessage = function(e){ if(e.data === "get"){ port.postMessage(data); }else{ data = e.data; port.postMessage("worker: " + data); // 后续处理 } } }   Dedicated Worker与Shared Worker的本质区别:   专用Worker只属于某个页面,不会和其他页面的Render进程(浏览器内核进程)共享,所以在Render进程中创建一个新的线程来运行Worker中的JavaScript程序;   SharedWorker是浏览器所有页面共享的,不能采用与Worker同样的方式实现,因为它不隶属于某个Render进程,可以为多个Render进程共享使用,所以浏览器为SharedWorker单独创建一个进程来运行JavaScript程序,在浏览器中使用相同名字的SharedWorker的多个脚本,只存在一个SharedWorker进程,不管它被创建多少次;   共享worker身份由解析脚本URL、worker名和文档源确定;new SharedWorker("./sharedWorker.js"); new SharedWorker("./sharedWorker.js"); new SharedWorker("./sharedWorker.js"); new SharedWorker("./sharedWorker.js"); new SharedWorker("sharedWorker.js"); new SharedWorker("http://localhost/sharedWorker.js");   即使加载的是同一个脚本,但解析出来的URL不一致,也不能共享,如:new SharedWorker("./sharedWorker.js"); new SharedWorker("./sharedWorker.js?");   因为可选的worker名是共享worker身份的一部分,使用不同的worker名会让浏览器强制创建多个共享worker,尽管它们的源和脚本URL相同;new SharedWorker("./sharedWorker.js", {name: "one"}); new SharedWorker("./sharedWorker.js", {name: "one"}); new SharedWorker("./sharedWorker.js", {name: "two"});   共享worker可以在标签、窗口、iframe或其他运行在同一个源的worker之间共享; 如:index.html   SharedWorker也可以使用importScripts()方法;但不能使用共享subworker,在某些浏览器中,可以使用专用subworker,例如Firefox可以使用,如:主页面:var sharedWorker = new SharedWorker("worker.js"); sharedWorker.port.onmessage = function(event){ console.log("Main: " + event.data); };   worker.js:importScripts("w1.js","w2.js"); onconnect = function (e) { var port = e.ports[0]; port.postMessage("返给主页面,我用到了subworker"); var subworker = new Worker("subworker.js"); subworker.onmessage = function(event){ console.log("worker: " + event.data); port.postMessage("worker转发给main: " + event.data); }; subworker.postMessage("worker发送给subworker"); }   subworker.js:onmessage = function(event){ console.log("subworker: " + event.data); postMessage("subworker发送给worker"); };   在一个脚本中,可以同时使用专用Worker和共享Worker;如:主页面index.html:var worker = new Worker("worker.js"); var sharedWorker = new SharedWorker("sharedworker.js"); worker.onmessage = function(event){ console.log(event.data); // 再扔给sharedWorker sharedWorker.port.postMessage(event.data); sharedWorker.port.onmessage = function(e){ console.log(e.data); }; }; // 给专用worker一个对象 worker.postMessage({name:"wangwei",age:18});   worker线程:onmessage = function(event){ var person = event.data; // 进行必要的处理,如: person.sex = true; person.company = { name: "零点程序员", address: "北京东城" } // 加工一下,再送回去 postMessage(person); };   sharedworker.js:var obj; onconnect = function(event){ var port = event.ports[0]; port.onmessage = function(e){ if(!obj) obj = e.data; port.postMessage(obj); } }   close()方法:断开端口连接,它将不再是激活状态;如:// 主页面: worker.port.close() // 仅仅关闭连接 // SharedWorker内部: port.close() // 仅仅关闭连接   不能人为终止共享SharedWorker,在MessagPort对象上调用close()也不会导致共享worker的终止,它仅仅是断开连接,并不会销毁SharedWorker,即使只有一个页面和共享worker连接,其还会持续运行;   另外SharedWorker没有类似terminate()的方法;其运行的状态与相应的MessagePort或MessageChannel的状态无关;一旦建立并连接SharedWorker,连接的管理就由浏览器负责,并且所建立的连接将持续在页面的生命周期中持续,只有全部页面销毁,没有连接的时候,且该SharedWorker内部没有运行中的任务,浏览器才终止该SharedWorker;   Worker(专用和共享)内部中的事件,主线程是没法监听到的,反之亦然;Worker中的异常,主线程也是无法感知的,反之亦然;二者唯一的交互方式就是postMessage和监听message事件;如:主页面:   sharedworker:var connectList = []; onconnect = function(event){ var port = event.ports[0]; port.onmessage = function(e){ port.postMessage("收到消息: " + e.data); if(connectList.indexOf(port) === -1) connectList.push(port); connectList.forEach(function(v,i){ v.postMessage("现在有连接: " + connectList.length); }) } }   清除Shared Worker:   在页面关闭后,workerPool中的port并不会自动清除,会造成内存的占用;通过监听页面关闭或页面生命周期的window.onbeforeunload,通知SharedWorker关闭此连接;如:主页面:var btnClose = document.getElementById("btnClose"); btnClose.onclick = function(){ sharedWorker.port.postMessage({action: "close"}); sharedWorker.port.close(); console.log("sharedWorker已关闭"); }   sharedworker.jsvar connectList = []; onconnect = function(event){ var port = event.ports[0]; port.onmessage = function(e){ if(connectList.indexOf(port) === -1) connectList.push(port); if(e.data.action == "close"){ port.close(); connectList = connectList.filter(function(item){ return item != port; }) } connectList.forEach(function(v,i){ v.postMessage("现在有连接: " + connectList.length); }); } }   当关闭窗口时,包括刷新重新载入,也需要通知SharedWorker,如:主页面添加:window.onbeforeunload = function(){ sharedWorker.port.postMessage({ action: "close" }) }   错误的处理:   SharedWorker内部的错误,无论是其内部还是主页面都无法获取,如:主页面:var sharedWorker = new SharedWorker("sharedworker.js"); console.log(sharedWorker); console.log(sharedWorker.port); sharedWorker.port.postMessage("随便发点啥"); sharedWorker.port.onmessage = function(event){ console.log(event.data); }; sharedWorker.onerror = function(event){ console.log(event); }; sharedWorker.port.onerror = function(event){ console.log(event); }; sharedWorker.port.onmessageerror = function(event){ console.log(event); };   sharedworker.js:onconnect = function(event){ var port = event.ports[0]; console.log(port); port.onmessage = function(e){ throw new Error("有错了"); }; port.onmessageerror = function(event){ console.log(event); } }   sharedWorker.onerror,在共享工作进程中发生错误时触发;   port.onmessageerror事件,当MessagePort对象接收到无法反序列化的消息时触发;   示例:计算两个数的积var first = document.querySelector("#number1"); var second = document.querySelector("#number2"); var result = document.querySelector(".result"); if(!!window.SharedWorker) { var myWorker = new SharedWorker("sharedworker.js"); first.onchange = function() { myWorker.port.postMessage([first.value, second.value]); console.log("向worker中发送第一个乘数"); } second.onchange = function() { myWorker.port.postMessage([first.value, second.value]); console.log("向worker中发送第二个乘数"); } myWorker.port.onmessage = function(event) { result.textContent = event.data; console.log("从worker接收到计算结果:" + event.data); } }else{ result.textContent = "浏览器不支持 web workers"; }   sharedworker2.html:

上一次结果: - sharedworker.js: var result = 0; onconnect = function(event) { var port = event.ports[0]; port.postMessage(result); port.onmessage = function(e) { result = e.data[0] * e.data[1]; port.postMessage(result); } }   在SharedWorker中,利用ports属性向所有连接页面分发消息,如:

worker2.html   sharedworker.js:var connectList = []; var textList = []; onconnect = function(event){ var port = event.ports[0]; port.onmessage = function(e){ if(connectList.indexOf(port) === -1) connectList.push(port); switch(e.data.status){ case 0: postMsg(function(item){ if(item != port) item.postMessage("有新用户加入"); else item.postMessage("我是新用户"); }); break; default: textList.push(e.data.value); postMsg(textList); break; } } } // 分发消息 function postMsg(callback){ var callback = (typeof callback === "function") ? callback : function(item){ item.postMessage(callback); } connectList.forEach(callback); }   示例:页面之间登录状态通知:主页面: login info...   login.html登录页:   sharedworker.js:var isLogin = false; var connectList = []; var tipsTextArray = [ "You signed in another tab or window. Reload to refresh your session.", "You signed out in another tab or window. Reload to refresh your session." ]; onconnect = function(event){ var port = event.ports[0]; if(connectList.indexOf(port) === -1) connectList.push(port); port.onmessage = function(e){ isLogin = e.data.isLogin; tipsText = isLogin ? tipsTextArray[0] : tipsTextArray[1]; connectList.forEach(function(item){ item.postMessage({isLogin: isLogin, tipsText: tipsText}); }) }; }


太原有机会发展成像西安那样的旅游规模吗,为什么?单从旅游规模上来说,太原想要发展到和西安差不多,的确很难。为什么这么说呢?我认为,原因有很多,但是归根结底,最重要的原因在于,太原这座城市现有的旅游资源还难以和西安相比。也就是说,DNF低成本刷商城积分,80块可换3000积分,是真的吗?怎么操作?不知道大家对今年的积分商城有什么想法,其中的属强宝珠给小号用其实是非常不错的,但关键是积分不好弄啊,如果只是买年套,400块才400积分,那就起码要5套才能换3个15属强宝珠,肯定足跟痛怎么办?足跟痛主要有三种类型。1跖筋膜炎。这种类型的跟痛症,主要是因为过多行走等活动,造成跟部足底区跖筋膜止点处滑囊炎。其本质是滑囊炎,所以在治疗上,只要针对性消除局部无菌性炎症,就能迅速学了心理学之后,你最大的感悟是什么呢?大家好,我是渡仁心理的小渡渡,大学时候的专业是应用心理学。大家似乎对心理学都有一个误解,每次一给朋友说我学心理学,就有这样的对话你学心理?嗯。我挺好奇,想问问我不知道你在想什么,催为什么辽宁男篮的球迷那么多?1。长的帅颜值担当杨鸣,CBA第一帅,主要负责帅。在辽宁队服役多年,吸引粉丝无数,尤其是全国各地的女粉丝。其他颜值担当赵继伟,张镇麟,吴昌泽,李晓旭,郭艾伦。每一位颜值担当都是一个你最讨厌班级里什么样的同学?作为一名教师兼班主任,我本不该将班里的孩子区别对待。或者说,我可以欣赏喜欢某一位同学,却不能不喜欢另一位同学。因为教师这一特殊身份,我有时候得在内心做激烈的斗争,告诫自己对具有某些有哪些看一眼就想收藏的句子?1。秋风生凉意,微光暖人心。2。所有人的坚强,都是柔软生的茧。3。秋天,或许是个会让人想写信的季节。4。谁能一如既往,不改初见模样。5。有梦想的人,永远年少。6。别太善解人意,也别DNF心悦APP给我推荐DNF手游资格是真的还是假的,大家有被推荐到吗?DNF手游一直以来都是玩家比较关注的话题,从去年公测结束到目前也有半年多的时间了,公测结束后策划在发布会上也对玩家承诺了今年的上半年开放,可是策划说的话跟放屁一样,目前已经到了下半国乒男队每一代都是三驾马车,为什么樊振东这一代就他一枝独秀?众所周知,国乒人才辈出,不管是男队还是女队,从乒乓球技术上来讲,都是江山代有才人出,各领风骚数百年。尤其是男队,很多世界冠军退役之后都只能留在队里当厨子。二王一马一胖子自从双子星刘华为5G那么牛,为什么新出的手机不支持5G呢?因为5G射频芯片也被卡了,不能接受5G信号,不过现在有消息明年应该就有国产替代了,华为真难,而且以前中国的射频技术是世界顶尖的,也是被美国把那个公司创始人骗到美国,以莫须有的罪名抓惯子如杀子,溺爱出逆子。你见过被宠到无药可救的巨婴吗?30岁男子玩游戏年入30w,妻子却执意离婚。妻子我像带儿子一样带他,他吃饭还要他妈夹菜不夹不吃!母亲他怎么舒服怎么过!家住长沙的段芬女士给电视台打来电话,说儿媳去娘家6个月不回,还
把0028的火车当成晚上1228的了,赶错火车什么感受?二十多年前我身上就发生过一次这样的事情,当时的情况是我和两个朋友准备从兰州往西安赶,因为我们当时各自都是需要在西安转车的。记得当时应该是九月份吧!我们决定回老家的时候,便决定先将车不要抹黑,谁能告诉我机械类一般月薪如何?以我自已真实写照,本人学历低,一直在一个厂干,做机械加工的,快十年了吧,因为是内陆城市(河南省会),工资不能跟沿海相比,从开始的一千多当学徒到现在一万左右,也是经过时间的考验的,青登月宇航员,返回后为什么曾出现怪病?月球有毒吗?为什么登上月球的宇航员都出现怪病?在阿波罗载人登月任务实施之前,NASA就制定了严格登月的检验检疫程序,不仅是登月前的隔离准备工作哦,还有登月回来后隔离检疫,为此还专门云服务器环境要怎么配置?你购买腾讯云服务器的时候,应该已经选择好操作系统的类型了,希望你选择的是Linux云服务器。配置服务器环境,我推荐你使用宝塔Linux面板,只要在腾讯云服务器上安装上宝塔,剩下的环一天吃两次谷维素B1效果怎么样?谷维素和维生素B1,都是身体神经系统健康所需的两种营养素,有些有植物神经紊乱情况的朋友,医生会建议结合情况,适量的补充这2种营养素,那么这两种营养素会有什么效果,长期吃身体又会有哪艾灸有排病反应,你见过最严重的排病反应是什么样的?我坚持艾灸6年,我来回答。我本身体寒,月子里又进了风,可以说一身风寒,最严重的是坐骨,夏不能倚石,冬不能触木,一坐下去整个屁股就感受冷气嗖嗖地灌进去,冬天穿三条厚裤子加长过膝的羽绒网贷被起诉,法院判决利息是按照15。4,还是24,如何判?1。首先,看主体,出借人如果是持牌机构,按照最高院对广东高院的答复,不适用民间借贷司法解释的规定,也就不适用4倍LPR上限的计算方式2。其次,如果排除上述1,则要看借贷行为发生时间工厂招聘40007000,到底是相信4000还是7000,有没有知道的人?你好,以我的招聘经历来看,我认为工厂招聘写的工资为40007000是可信的,4000可以信,7000也可以信。为了还原招聘工资40007000的可信度,我将从岗位性质工厂生产经营状早产的小孩会笨吗?我家真真实实的早产宝宝,怀孕28周出生只有2斤,我想我最有权利回答这个问题了。2018年末次月经6月6号,预产期是次年3月21。但是2018年12月21号宝宝早产。别人都是3740学霸的家长,孩子倒数是种什么体验?这事儿说开了挺伤人,不说又会误导人。父母学霸,孩子学渣,这种段子比比皆是。我相信真实存在,但那是极少极少的,大多数都是假新闻!但是往开了说,这事很伤人。因为学霸父母拍桌子,是因为他幼儿入园产生分离焦虑的因素是什么?分离焦虑简单来讲,就是婴幼儿离开亲人尤其是婴幼儿时期主要养育者(如爸爸妈妈爷爷奶奶甚至保姆等)时,表现出来的不安急躁焦虑失落等不愉快的情绪反应,或者有难以适应和调节的行为表现。分离