什么是Web Worker?

web worker 是运行在后台的 JavaScript,不占用浏览器自身线程,独立于其他脚本,可以提高应用的总体性能,并且提升用户体验。

一般来说Javascript和UI页面会共用一个线程,在HTML页面中执行js脚本时,页面的状态是不可响应的,直到脚本已完成。而这段代码可以交给Web Worker在后台运行,那么页面在Javascript运行期间依然可以响应用户操作。后台会启动一个worker线程来执行这段代码,用户可以创建多个worker线程。

有两种 Web Worker

Web workers可分为两种类型:专用线程dedicated web worker,以及共享线程shared web worker。 Dedicated web worker随当前页面的关闭而结束;这意味着Dedicated web worker只能被创建它的页面访问。与之相对应的Shared web worker可以被多个页面访问。在Javascript代码中,“Work”类型代表Dedicated web worker,而“SharedWorker”类型代表Shared web worker。

在绝大多数情况下,使用Dedicated web worker就足够了,因为一般来说在web worker中运行的代码是专为当前页面服务的。而在一些特定情况下,web worker可能运行的是更为普遍性的代码,可以为多个页面服务。在这种情况下,我们会创建一个共享线程的Shared web worker,它可以被与之相关联的多个页面访问,只有当所有关联的的页面都关闭的时候,该Shared web worker才会结束。相对Dedicated web worker,shared web worker稍微复杂些。

new Worker()对象代表Dedicated Web Worker,以下示例代码都为Dedicated Web Worker。

如何创建 Web Worker?

创建一个新的 worker 十分简单。你所要做的就是调用 Worker() 构造函数,指定一个要在 worker 线程内运行的脚本的 URI,如果你希望能够与worker进行通信,接收其传递回来的数据,可以将worker的onmessage属性设置成一个特定的事件处理函数,当 web worker 传递消息时,会执行事件监听器中的代码。event.data 中存有来自 worker 的数据。。

example.html: (主页面):

  1. var myWorker = new Worker("worker_demo.js");
  2.  
  3. myWorker.onmessage = function (event) {
  4. console.log("Called back by the worker!\n");
  5. };

或者,也可以使用 addEventListener()添加事件监听器:

  1. var myWorker = new Worker("worker_demo.js");
  2.  
  3. myWorker.addEventListener("message", function (event) {
  4. console.log("Worker said : " + event.data);
  5. }, false);
  6.  
  7. myWorker.postMessage("hello my worker"); // start the worker.

例子中的第一行创建了一个新的 worker 线程。第三行为 worker 设置了 message 事件的监听函数。当 worker 调用自己的 postMessage() 函数时就会向后台Worker发送数据,并且后台返回消息调用message这个事件处理函数。

注意: 传入 Worker 构造函数的参数 URI 必须遵循同源策略为了高效地传输 ArrayBuffer 对象数据,需要在 postMessage 方法中的第二个参数中指定它。实例代码如下:

  1. myWorker.postMessage({
  2. operation: 'list_all_users',
  3. //ArrayBuffer object
  4. input: buffer,
  5. threshold: 0.8,
  6. }, [buffer]);

worker_demo.js (worker):

  1. postMessage("I\'m working before postMessage(\'hello my worker\').");
  2.  
  3. onmessage = function (event) {
  4. postMessage("Hi " + event.data);
  5. };

注意: 通常来说,后台线程 – 包括 worker – 无法操作 DOM。 如果后台线程需要修改 DOM,那么它应该将消息发送给它的创建者,让创建者来完成这些操作。

通过Web Worker你可以在前台做一些小规模分布式计算之类的工作,不过Web Worker有以下一些使用限制:

  • Web Worker无法访问DOM节点;
  • Web Worker无法访问全局变量或是全局函数;
  • Web Worker无法访问window、document之类的浏览器全局变量、方法;

不过Web Worker作用域中依然可以使用有:

  • 定时器相关方法 setTimeout(),clearTimeout(),setInterval()...之类的函数
  • navigator对象,它含有如下能够识别浏览器的字符串,就像在普通脚本中做的那样,如:appName、appVersion、userAgent...
  • 引入脚本与库,Worker 线程能够访问一个全局函数,importScripts() ,该函数允许 worker 将脚本或库引入自己的作用域内。你可以不传入参数,或传入多个脚本的 URI 来引入;以下的例子都是合法的:
    1. importScripts(); /* 什么都不引入 */
    2. importScripts('foo.js'); /* 只引入 "foo.js" */
    3. importScripts('foo.js', 'bar.js'); /* 引入两个脚本 */

    浏览器将列出的脚本加载并运行。每个脚本中的全局对象都能够被 worker 使用。如果脚本无法加载,将抛出 NETWORK_ERROR 异常,接下来的代码也无法执行。而之前执行的代码(包括使用setTimeout延迟执行的代码)却依然能够使用。importScripts()之后的函数声明依然能够使用,因为它们始终会在其他代码之前运行。
    注意: 脚本的下载顺序不固定,但执行时会按照你将文件名传入到importScripts()中的顺序。这是同步完成的;直到所有脚本都下载并运行完毕,importScripts()才会返回。

  • atob() 、btoa()  base64编码与解码的方法。
  • 也可以使用XMLHttpRequest对象来做Ajax通信,以及其他API:WebSocket、Promise、Worker(可以在Worker中使用Worker)
    下面简单写下
    Web Worker使用XMLHttpRequest与服务端通信:
    1. addEventListener("message", function(evt){
    2. var xhr = new XMLHttpRequest();
    3. xhr.open("GET", "serviceUrl"); //serviceUrl为后端j返回son数据的接口
    4. xhr.onload = function(){
    5. postMessage(xhr.responseText);
    6. };
    7. xhr.send();
    8. },false);

    上述举例的代码有些简陋,只是为了抛砖引玉,见谅。其他API与Web Worker的融合使用也是大同小异,大家可以自己琢磨琢磨。

终止 web worker

如果你想立即终止一个运行中的 worker,可以调用 worker 的terminate()方法。被终止的Worker对象不能被重启或重用,我们只能新建另一个Worker实例来执行新的任务。

  1. myWorker.terminate();

处理错误

当 worker 出现运行时错误时,它的onerror事件处理函数会被调用。它会收到一个实现了ErrorEvent接口名为error的事件,供开发者捕捉错误信息。下面的代码展示了如何绑定error事件:

  1. worker.addEventListener("error", function(evt){
  2. alert("Line #" + evt.lineno + " - " + evt.message + " in " + evt.filename);
  3. }, false);

如上可见, Worker对象可以绑定error事件;而且evt对象中包含错误所在的代码文件(evt.filename)、错误所在的代码行数(evt.lineno)、以及错误信息(evt.message)。

下面上一个完整的dedicated web worker 使用案例。

demo_worker.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>dedicated web worker</title>
  6. </head>
  7. <body>
  8. <p>Count numbers:
  9. <output id="result"></output>
  10. </p>
  11. <button id="startWorker">startWorker</button>
  12. <button id="endWorker">stopWorker</button>
  13. </body>
  14. <script>
  15. (function () {
  16. var result = document.querySelector('#result'),
  17. startWorker = document.querySelector('#startWorker'),
  18. endWorker = document.querySelector('#endWorker'),
  19. worker,
  20. data = 10;
  21. startWorker.addEventListener('click', function (event) {
  22. if (typeof Worker !== 'undefined') {
  23. if (typeof worker == "undefined") {
  24. worker = new Worker('./demo_workers.js');
  25. }
  26. worker.addEventListener('message', function (event) {
  27. result.innerHTML = event.data;
  28. }, false);
  29. worker.addEventListener("error", function (event) {
  30. alert("Line #" + event.lineno + " - " + event.message + " in " + event.filename);
  31. }, false);
  32. worker.postMessage(data);
  33. endWorker.addEventListener('click', function () {
  34. worker.terminate();
  35. }, false);
  36. } else {
  37. result.innerHTML = 'sry, your browser does not support Web workers...';
  38. }
  39. }, false);
  40. })();
  41. </script>
  42. </html>

这个HTML页面中有个startWorker按钮,点击后会运行一个Javascript文件。上面的代码中首先检测当前浏览器是否支持Web Worker,不支持的话就显示提醒信息。

按钮的点击事件中创建了Worker对象,并给它指定了Javascript脚本文件——demo_workers.js(稍后会有代码),并且给Worker对象绑定了一个“message”事件。该事件会在后台代码(demo_workers.js)向页面返回数据时触发。“message”事件可以通过event.data来获取后台代码传回的数据。最后,postMessage方法正式执行demo_workers.js,该方法向后台代码传递参数,后台代码同样通过message事件参数的data属性获取。

demo_worker.js

  1. addEventListener('message',function (event) {
  2. var count = event.data;
  3. var interval = setInterval(function () {
  4. postMessage(count--);!count && clearInterval(interval);
  5. },1000);
  6.  
  7. });

以上代码在后台监听message事件,并获取页面传来的参数;这里实际上是一个从10到1的倒计时:在message事件被触发之后,把结果传给页面显示出来。

所以当点击startWorker按钮,页面会在count number: 显示从10递减一变为最终的1,在这10秒钟内页面依然可以响应鼠标键盘事件。点击stopWorker按钮,web worker 会直接终止,页面变化显示会直接停止。

嵌入式web worker

目前没有一种官方的方法能够像script标签一样将 worker 的代码嵌入的网页中。但是如果一个script元素没有指定src属性,并且它的type没有指定成一个可运行的 mime-type,那么它就会被认为是一个数据块元素,并且能够被 JavaScript 使用。数据块是 HTML5 中一个十分常见的特性,它可以携带几乎任何文本类型的数据。所以,你能够以如下方式嵌入一个 worker:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8" />
  5. <title>MDN Example - Embedded worker</title>
  6. <script type="text/js-worker">
  7. // 该脚本不会被 JS 引擎解析,因为它的 mime-type 是 text/js-worker。
  8. var myVar = "Hello World!";
  9. // 剩下的 worker 代码写到这里。
  10. </script>
  11. <script type="text/javascript">
  12. // 该脚本会被 JS 引擎解析,因为它的 mime-type 是 text/javascript。
  13. function pageLog (sMsg) {
  14. // 使用 fragment:这样浏览器只会进行一次渲染/重排。
  15. var oFragm = document.createDocumentFragment();
  16. oFragm.appendChild(document.createTextNode(sMsg));
  17. oFragm.appendChild(document.createElement("br"));
  18. document.querySelector("#logDisplay").appendChild(oFragm);
  19. }
  20. </script>
  21. <script type="text/js-worker">
  22. // 该脚本不会被 JS 引擎解析,因为它的 mime-type 是 text/js-worker。
  23. onmessage = function (oEvent) {
  24. postMessage(myVar);
  25. };
  26. // 剩下的 worker 代码写到这里。
  27. </script>
  28. <script type="text/javascript">
  29. // 该脚本会被 JS 引擎解析,因为它的 mime-type 是 text/javascript。
  30.  
  31. // 在过去...:
  32. // 我们使用 blob builder
  33. // ...但是现在我们使用 Blob...:
  34. var blob = new Blob(Array.prototype.map.call(document.querySelectorAll("script[type=\"text\/js-worker\"]"), function (oScript) { return oScript.textContent; }),{type: "text/javascript"});
  35.  
  36. // 创建一个新的 document.worker 属性,包含所有 "text/js-worker" 脚本。
  37. document.worker = new Worker(window.URL.createObjectURL(blob));
  38.  
  39. document.worker.onmessage = function (oEvent) {
  40. pageLog("Received: " + oEvent.data);
  41. };
  42.  
  43. // 启动 worker.
  44. window.onload = function() { document.worker.postMessage(""); };
  45. </script>
  46. </head>
  47. <body><div id="logDisplay"></div></body>
  48. </html>

现在,嵌入式 worker 已经嵌套进了一个自定义的 document.worker 属性中。

在 worker 内创建 worker

worker 的一个优势在于能够执行处理器密集型的运算而不会阻塞 UI 线程。在下面的例子中,worker 用于计算斐波那契数。

fibonacci.js

  1. var results = [];
  2. function resultReceiver(event) {
  3. results.push(parseInt(event.data));
  4. if (results.length == 2) {
  5. postMessage(results[0] + results[1]);
  6. }
  7. }
  8. function errorReceiver(event) {
  9. throw event.data;
  10. }
  11. onmessage = function(event) {
  12. var n = parseInt(event.data);
  13. if (n == 0 || n == 1) {
  14. postMessage(n);
  15. return;
  16. }
  17. for (var i = 1; i <= 2; i++) {
  18. var worker = new Worker("fibonacci.js");
  19. worker.onmessage = resultReceiver;
  20. worker.onerror = errorReceiver;
  21. worker.postMessage(n - i);
  22. }
  23. };

worker 将属性onmessage设置为一个函数,当worker对象调用 postMessage()时该函数会接收到发送过来的信息。(注意,这么使用并不等同于定义一个同名的全局变量,或是定义一个同名的函数。var onmessage function onmessage 将会定义与该名字相同的全局属性,但是它们不会注册能够接收从创建 worker 的网页发送过来的消息的函数。) 这会启用递归,生成自己的新拷贝来处理计算的每一个循环。

fibonacci.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8" />
  5. <title>Test threads fibonacci</title>
  6. </head>
  7. <body>
  8. <div id="result"></div>
  9. <script>
  10. var worker = new Worker("fibonacci.js");
  11. worker.onmessage = function(event) {
  12. document.getElementById("result").textContent = event.data;
  13. dump("Got: " + event.data + "\n");
  14. };
  15. worker.onerror = function(error) {
  16. dump("Worker error: " + error.message + "\n");
  17. throw error;
  18. };
  19. worker.postMessage("5");
  20. </script>
  21. </body>
  22. </html>

网页创建了一个div元素,ID为result,用它来显示运算结果,然后生成worker。在生成worker后,onmessage处理函数配置为通过设置div元素的内容来显示运算结果,最后,向worker发送一条信息来启动它。
注意:chrome下不支持在worker中创建worker、以及dump方法、所以上述代码可以在Firefox下运行。由于文章篇幅过长,关于共享线程shared web worker的介绍将在下篇文章Web Worker javascript多线程编程(二)发布。

Web Worker javascript多线程编程(一)的更多相关文章

  1. Web Worker javascript多线程编程(二)

    Web Worker javascript多线程编程(一)中提到有两种Web Worker:专用线程dedicated web worker,以及共享线程shared web worker.不过主要讲 ...

  2. 【转】Web Worker javascript多线程编程(一)

    原文:https://www.cnblogs.com/peakleo/p/6218823.html -------------------------------------------------- ...

  3. 深入理解javascript异步编程障眼法&&h5 web worker实现多线程

    0.从一道题说起 var t = true; setTimeout(function(){ t = false; }, 1000); while(t){ } alert('end'); 1 2 3 4 ...

  4. 转载:JavaScript多线程编程简介

    虽然有越来越多的网站在应用AJAX技术进行开发,但是构建一个复杂的AJAX应用仍然是一个难题.造成这些困难的主要原因是什么呢?是与服务器的异步通信问题?还是GUI程序设计问题呢?通常这两项工作都是由桌 ...

  5. JS利用HTML5的Web Worker实现多线程

    需求:有一个长时间的斐波拉契的计算希望放在分线程中计算,计算的得到结果后再返回给主线程展示,再计算的时候不冻结页面 var number = 55;//传入分线程的参数 var worker = ne ...

  6. 深入HTML5 Web Worker应用实践:多线程编程

    HTML5 中工作线程(Web Worker)简介 至 2008 年 W3C 制定出第一个 HTML5 草案开始,HTML5 承载了越来越多崭新的特性和功能.它不但强化了 Web 系统或网页的表现性能 ...

  7. 深入 HTML5 Web Worker 应用实践:多线程编程

    深入 HTML5 Web Worker 应用实践:多线程编程 HTML5 中工作线程(Web Worker)简介 至 2008 年 W3C 制定出第一个 HTML5 草案开始,HTML5 承载了越来越 ...

  8. 【转向Javascript系列】深入理解Web Worker

    本文首发在alloyteam团队博客,链接地址http://www.alloyteam.com/2015/11/deep-in-web-worker/ 上一篇文章<从setTimeout说事件循 ...

  9. 文档通信(跨域-不跨域)、时时通信(websocket)、离线存储(applicationCache)、开启多线程(web worker)

    一.文档间的通信 postMessage对象 //不跨域 1.iframe:obj.contentWindow [iframe中的window对象] iframe拿到父级页面的window: pare ...

随机推荐

  1. 学习C的笔记

    [unsigned] 16位系统中一个int能存储的数据的范围为-32768~32767,而unsigned能存储的数据范围则是0~65535.由于在计算机中,整数是以补码形式存放的.根据最高位的不同 ...

  2. SQL 约束

    先用设计器创建约束.再用代码创建约束.数据库约束是为了保证数据的完整性(正确性)而实现的一套机制见文件Employee.sql非空约束(选择复选框)主键约束(PK) primary key const ...

  3. postgresql 基本语法

    postgresql数据库创建/修改/删除等写入类代码语法总结: 1,创建库 2,创建/删除表 2.1 创建表 create table myTableName 2.2 如果表不存在则创建表 crea ...

  4. 在Linux虚拟机下配置jdk的环境变量

    1.到Oracle公司的官网里下载好jdk,网址 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133 ...

  5. 站在风口,你或许就是那年薪20w+的程序猿

    最近面试了一些人,也在群上跟一些群友聊起,发现现在的互联网真是热,一些工作才两三年的期望的薪资都是十几K的起,这真是让我们这些早几年就成为程序猿的情何以堪!正所谓是站在风口上,猪也能飞起来!我在这里就 ...

  6. [Intel Edison开发板] 01、Edison开发板性能简述

    Integrated Wi-Fi certified in 68 countries, Bluetooth® 4.0 support, 1GB DDR and 4GB flash memory sim ...

  7. 详解this

    this 虐我千百遍,看完此文效立见!不得不说,这篇文章的总结很地道很全面,适合收藏之用. 原文:all this 习惯了高级语言的你或许觉得JavaScript中的this跟Java这些面向对象语言 ...

  8. [Javascript] 爬虫 模拟新浪微博登陆

     概述: 由于业务需要,要编写爬虫代码去爬去新浪微博用户的信息. 虽然在网上能找到不少信息,但由于新浪微博改版,其登陆机制进行了修改,故很多老的文章就不适合用了. 经过一番摸索,成功模拟新浪微博的登陆 ...

  9. Python初学者之网络爬虫

    声明:本文内容和涉及到的代码仅限于个人学习,任何人不得作为商业用途. 本文将介绍我最近在学习Python过程中写的一个爬虫程序,将力争做到不需要有任何Python基础的程序员都能读懂.读者也可以先跳到 ...

  10. python的print函数的格式化输出

    使用print函数的时候,可以像C一样格式化输出,同时还支持参数化输出 print('%s' % ("CooMark")) print('整数|%d|' % (123)) prin ...