WebWorker(工作线程)笔记

来源:互联网 发布:如何评价林俊杰 知乎 编辑:程序博客网 时间:2024/05/22 12:33

WebWorker简介

  1. 产生原因:js是单线程,比如当js运行一段代码时,如果这段代码没有运行完成,是无法做其他的操作。随着HTML要处理的任务越来越重,单线程有时满足使用要求;
  2. 概念:Web Worker允许开发人员编写能长时间运行而不被用户中断的后台程序,去执行事务或者逻辑,并同时保证页面对用户的及时响应。简言之,即允许js创建多个线程,但子线程完全受主线程控制,而且子线程不能操作DOM;
  3. 特点:能够长时间运行(响应),理想的启动性能以及理想的内存消耗;
  4. 使用说明:
    • 无法访问DOM;
    • 无法访问全局变量或全局函数(只能访问当前js中的函数);
    • 无法调用alert或者confirm等函数;
    • 无法访问window、document之类的浏览器全局变量;
    • 可以使用setTimeout、setInterval;
    • 可以使用XMLHttpRequest对象来做Ajax通信。
    • 通过new Work(url)来创建work对象,加载的js文件不能跨域;
    • 如果使用了WebWorker,必须将html页面部署到服务器上;

WebWorker原理

传统的线程可解释为轻量级进程,它和进程一样有独立的执行控制,一般情况下由操作系统负责调度。在HTML5中的多线程是指允许web程序并发的执行js脚本,每个脚本执行流都称为一个线程,彼此间相互独立,并由浏览器中的js引擎负责管理

WorkerGlobalScope全局上下文

主页面html执行的全局上下文是window,而WebWorker执行的全局上下文是WorkerGlobalScope(不知道是啥),所以无法访问window对象,其属性与window中的属性类似;WorkerGlobalScope作用域下常见的属性如下
1. self:表示对WorkerGlobalScope对象本身的引用,专用线程中执行结果为DedicatedWorkerGlobalScope,共享线程中执行结果为SharedWorkerGlobalScope;
2. location:表示用于初始化这个工作线程的脚本资源的绝对 URL;
3. close:关闭当前线程,与terminate作用类似。步骤:删除这个工作线程事件队列中的所有任务,设置 WorkerGlobalScope 对象的 closing 状态为 true (这将阻止以后任何新的任务继续添加到事件队列中来);
4. importScripts:可以通过importScripts批量加载js文件;
5. XMLHttpRequest:通过该对象发送Ajax请求;
6. setTimeout/setInterval:用法与window中一样;
7. addEventListener(onmessage)/postMessage:addEventListener方法可以代替onmessage

共享线程(Shared Worker)和专用线程(Dedicated Worker)

区别:
1. 专用线程会使用隐式的 MessagePort 实例,MessagePort对象支持 HTML5 中多线程提供的所有功能。当专用线程被创建的时候,MessagePort 的端口消息队列便被主动启用;而共享线程必须显式使用。
2. 创建方法:专用线程只有URL一个参数;而共享线程创建是如果提供了第二个参数,那么这个参数将被用于作为这个共享线程的名称。
3. Woker对象和SharedWorker对象
这里写图片描述
这里写图片描述

//专用线程var worker=new Worker('worker.js');worker.onmessage=function(){};worker.postMessage('123');worker.postMessage(data,[data]);//如果data为ArrayBuffer数据时,需要在第二个参数中指定它以提高传输效率;//共享线程var worker=new SharedWorker('SharedWorker.js','myShareWorker');worker.port.addEventListener('message',function(e){});worker.port.start();//在SharedWorker中使用addEventListener时,必须用worker.port.start()来启动这个端口worker.port.postMessage('123');

WebWorker之常用API

worker线程从上到下同步运行它的代码,worker线程从上到下同步运行它的代码,如果worker注册了message事件处理程序,只要其有可能触发,worker就一直在内存中,不会退出,所以通信完毕后得手动在主线程中terminate或者子线程中close掉,但如果worker没有监听消息,那么当所有任务执行完毕(包括计数器)后,他就会退出。

  • 1. postMeassge(data):
    子线程与主线程互相传递消息使用的方法
  • //var worker =new Worker('worker.js');//主线程传消息给子线程worker.postMessage(data1);//在worker.js文件中,子线程传消息给主线程postMessage(data2);

  • 2. terminate():
    主线程中终止worker,此后无法再利用其进行消息传递。注意:一旦terminate后,无法重新启用,只能另外创建
  • //var worker =new Worker('worker.js');worker.terminate();

  • 3. onmessage:
    主线程、子线程接收消息。可以用addEventListener代替
  • //var worker =new Worker('worker.js');//主线程接收来自子线程的消息 worker.onmessage=function(e){    var data=e.data;    //e为MessageEvent对象};//在worker.js文件中,子线程接收来自主线程的消息onmessage=function(e){    var data=e.data;};

  • 4. MessageEvent:接收消息时用到,消息的实际传递内容为e.data;
  • 5. message:当有消息发送时,触发该事件。且,消息发送是双向的,消息内容可通过data来获取;
  • 6. error:出错处理。且错误消息可以通过e.message来获取。
  • //var worker =new Worker('worker.js');worker.onerror = function(e){    //打印出错消息    console.log(e.message);    //中断与子线程的联系    worker.terminate();}

    WebWorker优缺点:

    优点:
    1. 可以加载一个JS进行大量的复杂计算而不挂起主进程,并通过postMessage,onmessage进行通信
    2. 可以在worker中通过importScripts(url)加载另外的脚本文件
    3. 可以使用 setTimeout(), clearTimeout(), setInterval(), and clearInterval()
    4. 可以使用XMLHttpRequest来发送请求
    5. 可以访问navigator的部分属性

    缺点:
    1. 不能跨域加载JS(html主页面可以)
    2. worker内代码不能访问DOM
    3. 各个浏览器对Worker的实现不大一致,例如FF里允许worker中创建新的worker,而Chrome中就不行

    WebWorker应用

  • 1. 后台计算:
  • /**创建一个共享线程用于接收从不同连接发送过来的指令,指令结果处理完成后将结果返回到各个不同的连接用户*///Html文件<!Doctype html><html>    <head>        <meta charset='utf-8' />        <title>Calculate Number</title>     </head>    <body>        <div> The last common multiple and greateset common divisor is :        <p id='result'>Please wait,computing......</p>        </div><script type='text/javascript'>    var worker=new Worker('js/webworker.js');    worker.onmessage=function(e){//主线程接收来自子线程的消息        document.getElementById('result').innerHTML=e.data;    }    worker.postMessage({first:100,second:50});//主线程向子线程发消息    worker.onerror=function(){            alert('线程出错!');//异常处理        }</script>    </body></html>//webworker.js文件onmessage=function(e){  //子线程接收来自主线程的消息    var data=e.data;    Calculate(data.first,data.second);}function Calculate(first,second){    var common_divisor=divisor(first,second);    var common_multiple=multiple(first,second);    postMessage("Work done! " +"The least common multiple is "+common_divisor +" and the greatest common divisor is "+common_multiple); //子线程向主线程发消息}function divisor(a, b) {    if (a % b == 0) return b;    else return divisor(b, a % b); } function multiple( a,  b) {    var multiple = 0;    multiple = a * b / divisor(a, b);    return multiple; } 

  • 2. 使用共享线程处理多用户并发连接
    由于线程的构建以及销毁都要消耗很多的系统性能,例如 CPU 的处理器调度,内存的占用回收等,在一般的编程语言中都会有线程池的概念,线程池是一种对多线程并发处理的形式,在处理过程中系统将所有的任务添加到一个任务队列,然后在构建好线程池以后自动启动这些任务。处理完任务后再把线程收回到线程池中,用于下一次任务调用。线程池也是共享线程的一种应用。 (Web2D中有专用线程实现线程池的用法,即创建4个专用线程)
  • <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Shared worker example: how to use shared worker in HTML5</title> <script> var worker = new SharedWorker('sharedworker.js'); //创建线程 var log = document.getElementById('response_from_worker');  worker.port.addEventListener('message', function(e) {     log.textContent =e.data;  }, false);  worker.port.start(); //启动端口 worker.port.postMessage('ping from user web page..');  //Send user input to sharedworker  function postMessageToSharedWorker(input)  {      //Define a json object to construct the request      var instructions={             instruction:input.value        };      worker.port.postMessage(instructions);  } </script></head> <body onload=''> <output id='response_from_worker'>     How to use shared worker in HTML5!</output>     Send instructions to shared worker: <input type="text" autofocus            oninput="postMessageToSharedWorker(this);return false;"> </input> </body> </html>  /* * define a connect count to trace connecting * this variable will be shared within all connections */ //sharedworker.js文件var connect_number = 0; //记录连接到这个共享线程的总onconnect = function(e) {      connect_number =connect_number+ 1;      //get the first port here      var port = e.ports[0];      port.postMessage('A new connection! The current connection number is ' + connect_number);      port.onmessage = function(e) {         //get instructions from requester         var instruction=e.data.instruction;         var results=execute_instruction(instruction);        port.postMessage('Request: '+instruction+' Response '+results +' from shared worker...');  }; }; /* * this function will be used to execute the instructions send from requester * @param instruction * @return */ function execute_instruction(instruction) {     var result_value;     //implement your logic here     //execute the instruction...     return result_value }

  • 3.HTML5 线程代理
  • <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Shared worker example: how to use delegation worker in HTML5</title> <script>     var worker = new SharedWorker('delegationworker.js');      var log = document.getElementById('response_from_worker');      worker.onmessage = function (event) {      //resolve the population from delegation worker      var resultdata=event.data;      var population=resultdata.total_population;      var showtext='The total population of the word is '+population;  document.getElementById('response_from_worker').textContent = showtext;  }; </script></head> <body onload=''> <output id='response_from_worker'>     Shared worker example: how to use delegation worker in HTML5 </output> </body> </html>/* * define the country list in the whole word * take following Array as an example */ var country_list = ['Albania','Algeria','American','Andorra','Angola','Antigua','....']; // define the variable to record the population of the word var total_population=0; var country_size=country_list.length; var processing_size=country_list.length; for (var i = 0; i < country_size; i++) {      var worker = new Worker('subworker.js');      //wrap the command, send to delegations      var command={command:'start',country:country_list[i]};      worker.postMessage(command);      worker.onmessage = update_results; } /* * this function will be used to update the result * @param event * @return */ function storeResult(event) {     total_population += event.data;     processing_size -= 1;     if (processing_size <= 0)     {         //complete the whole work, post results to web page         postMessage(total_population);     } }//define the onmessage hander for the delegation onmessage = start_calculate; /* * resolve the command and kick off the calculation */ function start_calculate(event) {     var command=event.data.command;     if(command!=null&&command=='start')     {         var coutry=event.data.country;         do_calculate(country);     }     onmessage = null; } /* * the complex calculation method defined here * return the population of the country */ function do_calculate(country) {      var population = 0;      var cities=//get all the cities for this country      for (var i = 0; i < cities.length; i++)     {          var city_popu=0;          // perform the calculation for this city          //update the city_popu      population += city_popu;      }      postMessage(population);      close(); }
    原创粉丝点击