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

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


今天是我抄书的第三十六天,收益达到12。37在心中种花,人生才不会荒芜。不读书,不背诵,胸中无点墨,下笔难有神。如何让他人想你之所想,如何让自己得我之所得。方法之多,便不再一一详说。我从文中摘选精彩之处,希望对大家有所启示。人败不离懒,事败不离傲,家败不离怨孟子有言行有不得,反求诸己,其身正而天下归之。任何事情的结果,若与预期相悖,都要从自身找原因只要自身端正了,天下之人皆会归服。人这一生,是非成败,其实都在于自己。懒惰或勤奋骄傲或谦长大后,我就成了你寒来暑往,四序迁流,我已双鬓微白。从教三十年了,同事常夸我桃李满天下,我总喜欢报以微微一笑。耕耘于三尺讲台,我有过迷茫彷徨,更多的却是欢愉,是欣慰。每年教师节,我总会收到来自天南海上午10点,中国女足传喜讯,水庆霞激动,女足世界杯争冠抢占先机北京时间11月4日,距离卡塔尔世界杯开战的日子已经越来越近,虽然本届世界杯并没有中国男足的身影,但是不少中国足球迷们还是对于世界杯充满了浓厚的兴趣,当然大家也会对于2023年的女足妻子致信王大雷球场上错就是错必须认罚场外请你屡教不改直播吧11月4日讯今天,王大雷的妻子在个人社媒发文,就王大雷在与津门虎的比赛中染红表达了她的看法。昨天泰山与津门虎的比赛最后阶段,梅里达对孙准浩做出危险动作导致双方发生冲突,王大雷有一种等待,只为永恒的爱头条创作挑战赛冬季又来了,你却还没来,我只能继续等待,年复一年的等待,等到现在还只是一场空。命运还要让我等待多久,还要让我孤单多久?难道不能心疼我一下,让我如愿以偿,圆了我最美丽的好事不嫌晚,后登船者先上岸好事永远不嫌晚,因为好事多磨,后登船者先上岸,机会总是有的,只要把握住机会就一定会走向成功的。不管发生什么事,我们都要活在当下,为了当下而努力,不要想太多,思考太多就容易焦虑,每一有种饥渴,只有烈酒才能疗愈,这种饥渴就叫做孤独前言孤独是命运独有的待客方式在喧嚣的城市里,我们不停的游走,看着街道上的车水马龙,每一帧画面都在刺激着我们脆弱的神经,它像古老的钟表一样提醒着年轻人要时刻摆正自己的位置,避免成为孤时代在进步,女生一定要懂得投资自己NO。1不知你们曾经是否有过这种体验,当你站在漂亮衣服面前,极度渴望穿上却穿不上的窘迫当你进入高档餐厅却消费不起的尴尬当你因为自己容貌而自卑时的无奈。曾几何时,当我独自行走在街道上大器晚成的人,都有这三大突出特征,越到后面越容易成功有句话说世界上只有一种英雄主义,就是在认清生活真相后依然热爱生活。但是现实生活中,有那么一群人,他们并不是最优秀的,但他们却有着最耀眼的光芒。他们出生在普通家庭,没有资源,父母也都刘亦菲的8幅美图,每张皆是珍藏版1。关于恋爱的路,我们都曾经走过关于恋爱的歌,我们已听的太多关于心中的话,只对你一个人说。2。昨夜有繁星满天,今早有朝霞渐起。你看见也好,看不见也没关系,我找到你,它们才有意义。3
马赫雷斯与未婚妻健身秀恩爱,准岳父母原来这么出名30岁的曼城球星马赫雷斯,晒出与未婚妻泰勒沃德在健身房健身的照片,在应对球队比赛之余,他也用健身的方式和未婚妻亲密互动,简直是暴击单身狗们的操作。23岁泰勒沃德可是大有来头的人物,袁心玥抱惠若琪孩子被嘲笑,被拍成一米高,刘美君成下一个李盈莹目前,对于中国女排来说,今年最重要的事情,就是选择合适的中国女排主教练,毕竟,在中国女排前主教练郎平卸任之后,中国女排的主教练将是一件非常难产的事情,毕竟,在中国女排主教练的位置上三分丁,霸王鲨,核心高!山东三叉戟见雏形,哈神尴尬了?山东高速以9387战胜吉林,战绩改写为3胜2负。通过五场比赛来看,山东打得比较稳,该赢的比赛都拿下了,不该赢的都输了个稀里哗啦。在没有外援的情况下,高诗岩丁彦雨航陶汉林组成了山东新宋庆龄遗言不与孙中山合葬,她陪我53年,让她葬在我身边从古至今,我国素来讲究夫妻合葬之礼。即夫妻去世之后要葬在一起,这是现在一夫一妻制下的产物。意味着生前是夫妇,在去世之后也愿意住在一起,继续做夫妻,这也表达了夫妻两人之间伟大的爱情。三星GalaxyZFold35G虽然很昂贵,但是非常值三星第三代折叠屏真香自三星第三代折叠屏手机GalaxyZFold35G发布以来,这款手机便得到了不少消费者的关注,它利用全新的机身形态和高端的设计工艺,飞快蹿红成为了高端市场的明星快递网更密发展添活力来源人民网人民日报邮车行进在通往四川省汉源县永利乡古路村的悬崖山路上。新华社记者江宏景摄江西省赣州市安远县鹤子镇阳佳村,智慧物流运输快线机器人在空中穿梭。智运快线包括基站低空索道智炫酷机械者,美商海盗船K100璀璨金特别版机械键盘,上手体验导读自美商海盗船K100旗舰系列的键盘发售已有一年时间了,配备的高触发机械轴和其自研的CORSAIROPX光轴,性能和耐用性都十分优秀。今年,美商海盗船K100旗舰系列的键盘首次在经典与进化雷蛇炼狱蝰蛇V2X极速版无线鼠标大家好,我是波导终结者。雷蛇的炼狱蝰蛇是一款很经典的鼠标,很多朋友都对它有特殊的感情。我有个小伙伴当年也是用的初版蝰蛇玩WOW,这么多年了就算鼠标已经伊拉克战痕,都没舍得换。其实炼短小精悍,性能越级!iGameMini系列显卡再添新成员Mini系列的诞生源于ITX平台玩家对于高性能短卡的迫切需求,国创新生代高端硬件品牌iGame专为ITX平台推出的Mini短卡一经发布便引起DIY市场的热烈反响,Mini卡凭借其小星星千千万,哪颗最闪亮?解析星星明暗程度不一样的原因大家还记得小时候躺在草坪上看星星的场景吗?在远离城市的农村,夜幕降临之后,成千上万颗的星星在漆黑的夜空中熠熠生辉。我们最早认识的第一颗星星是北极星,因为大人们常说,它是夜空中最闪亮宏远新核心三人组已出现!阿联周鹏退居二线一新秀让杜锋非常失望北京时间10月24日,广东宏远将迎战土豪球队上海大鲨鱼,这是11冠王本赛季的第4场联赛,杜锋率队全力争取4连胜。纵观本赛季前3轮联赛,与上赛季相比,广东宏远出现不少不少变化。杜锋主