html5之Web Worker -- js多线程编程

什么是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 中存有来自 workera 的数据。。

example.html: (主页面):

var myWorker = new Worker("woker_demo.js");

myWorker.onmessage = function (oEvent) {
  console.log("Called back by the worker!\n");
};

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

var myWorker = new Worker("woker_demo.js");

myWorker.addEventListener("message", function (oEvent) {
  console.log("Worker said : " + oEvent.data);
}, false);

myWorker.postMessage("hello my worker"); // start the worker.

例子中的第一行创建了一个新的 worker 线程。第三行为 worker 设置了 message 事件的监听函数。当 worker 调用自己的 postMessage() 函数时就会调用这个事件处理函数。最后,第七行启动了 worker 线程。

注意: 传入 Worker 构造函数的参数 URI 必须遵循 同源策略

worker_demo.js (worker):

postMessage("I\‘m working before postMessage(\‘hello my worker\‘).");

onmessage = function (oEvent) {
  postMessage("Hi " + oEvent.data);
};

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

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

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

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

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

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

  • atob() 、btoa()  base64编码与解码的方法。
  • 也可以使用XMLHttpRequest对象来做Ajax通信,以及其他API:WebSocket、Promise、Worker

终止 web worker

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

myWorker.terminate();

处理错误

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

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

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

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

demo_worker.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<p>Count numbers:
    <output id="result"></output>
</p>
<button id="startWorker">startWorker</button>
<button id="endWorker">stopWorker</button>
</body>
<script>
    (function () {
        var result = document.querySelector(‘#result‘),
                startWorker = document.querySelector(‘#startWorker‘),
                endWorker = document.querySelector(‘#endWorker‘),
                worker,
                data = 10;
        startWorker.addEventListener(‘click‘, function (event) {
            //console.log(‘click event‘, event);
            if (typeof Worker !== ‘undefined‘) {
                if (typeof worker == "undefined") {
                    worker = new Worker(‘./demo_workers.js‘);
                }
                worker.addEventListener(‘message‘, function (event) {
                    //console.log(‘message event‘, event);
                    result.innerHTML = event.data;
                }, false);
                worker.addEventListener("error", function (event) {
                    alert("Line #" + event.lineno + " - " + event.message + " in " + event.filename);
                }, false);
                worker.postMessage(data);
                endWorker.addEventListener(‘click‘, function () {
                    worker.terminate();
                }, false);
            } else {
                result.innerHTML = ‘sry, your browser does not support Web workers...‘;
            }
        }, false);
    })();
</script>
</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

addEventListener(‘message‘,function (event) {
    var count = event.data;
    var interval = setInterval(function () {
        postMessage(count--);!count && clearInterval(interval);
    },1000);

});

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

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

嵌入式web worker

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>MDN Example - Embedded worker</title>
<script type="text/js-worker">
  // 该脚本不会被 JS 引擎解析,因为它的 mime-type 是 text/js-worker。
  var myVar = "Hello World!";
  // 剩下的 worker 代码写到这里。
</script>
<script type="text/javascript">
  // 该脚本会被 JS 引擎解析,因为它的 mime-type 是 text/javascript。
  function pageLog (sMsg) {
    // 使用 fragment:这样浏览器只会进行一次渲染/重排。
    var oFragm = document.createDocumentFragment();
    oFragm.appendChild(document.createTextNode(sMsg));
    oFragm.appendChild(document.createElement("br"));
    document.querySelector("#logDisplay").appendChild(oFragm);
  }
</script>
<script type="text/js-worker">
  // 该脚本不会被 JS 引擎解析,因为它的 mime-type 是 text/js-worker。
  onmessage = function (oEvent) {
    postMessage(myVar);
  };
  // 剩下的 worker 代码写到这里。
</script>
<script type="text/javascript">
  // 该脚本会被 JS 引擎解析,因为它的 mime-type 是 text/javascript。

  // 在过去...:
  // 我们使用 blob builder
  // ...但是现在我们使用 Blob...:
  var blob = new Blob(Array.prototype.map.call(document.querySelectorAll("script[type=\"text\/js-worker\"]"), function (oScript) { return oScript.textContent; }),{type: "text/javascript"});

  // 创建一个新的 document.worker 属性,包含所有 "text/js-worker" 脚本。
  document.worker = new Worker(window.URL.createObjectURL(blob));

  document.worker.onmessage = function (oEvent) {
    pageLog("Received: " + oEvent.data);
  };

  // 启动 worker.
  window.onload = function() { document.worker.postMessage(""); };
</script>
</head>
<body><div id="logDisplay"></div></body>
</html>

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

在 worker 内创建 worker

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

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

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

fibonacci.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8"  />
    <title>Test threads fibonacci</title>
  </head>
  <body>

  <div id="result"></div>

  <script>

    var worker = new Worker("fibonacci.js");

    worker.onmessage = function(event) {
      document.getElementById("result").textContent = event.data;
      dump("Got: " + event.data + "\n");
    };

    worker.onerror = function(error) {
      dump("Worker error: " + error.message + "\n");
      throw error;
    };

    worker.postMessage("5");

  </script>
  </body>
</html>

网页创建了一个 div 元素,ID 为 result , 用它来显示运算结果,然后生成 worker。在生成 worker 后,onmessage 处理函数配置为通过设置 div 元素的内容来显示运算结果,最后,向 worker 发送一条信息来启动它。

时间: 2024-10-15 07:20:22

html5之Web Worker -- js多线程编程的相关文章

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

需求:有一个长时间的斐波拉契的计算希望放在分线程中计算,计算的得到结果后再返回给主线程展示,再计算的时候不冻结页面 var number = 55;//传入分线程的参数 var worker = new Worker('worker.js');//利用一个js文件创建一个分线程 worker.postMessage(number);//向分线程传入数据 woker.onmessage=function(event){//设置分线程的回调函数,并接收返回的结果 console.log('接收到分线

通过一次实验来了解HTML5的 Web Worker

web worker 是运行在后台的 JavaScript,不会影响页面的性能. 当在 HTML 页面中执行脚本时,页面的状态是不可响应的,直到脚本已完成. web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能.您可以继续做任何愿意做的事情:点击.选取内容等等,而此时 web worker 在后台运行. 以上是W3School对WebWorker的定义,讲的非常明确.在这段解释中,我一眼就看到了一句话“执行脚本时,页面状态是不可响应的”,这让我想到了BO

Web Worker——js的多线程,实现统计博客园总阅读量

前言 众所周知,js是单线程的,从上往下,从左往右依次执行,当我们有耗时的任务需要处理时,便会阻塞线程造成页面卡顿等问题.web worker的目的,就是为JavaScript创造多线程环境,允许主线程将一些任务分配给子线程.在主线程运行的同时,子线程在后台运行,两者互不干扰.等到子线程完成计算任务,再把结果返回给主线程.因此,每一个子线程就好像一个“工人”(worker),默默地完成自己的工作.更多worker的介绍请戳:JavaScript标准参考教程 本文通过web worker 统计博客

html5的Web Worker和SSE(Server-Sent-Event)

web worker 是运行在后台的 JavaScript,不会影响页面的性能. 当在 HTML 页面中执行脚本时,页面的状态是不可响应的,直到脚本已完成. web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能.您可以继续做任何愿意做的事情:点击.选取内容等等,而此时 web worker 在后台运行. 创建 web worker 文件 现在,让我们在一个外部 JavaScript 中创建我们的 web worker. 在这里,我们创建了计数脚本.该脚本

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

深入 HTML5 Web Worker 应用实践:多线程编程 HTML5 中工作线程(Web Worker)简介 至 2008 年 W3C 制定出第一个 HTML5 草案开始,HTML5 承载了越来越多崭新的特性和功能.它不但强化了 Web 系统或网页的表现性能,而且还增加了对本地数据库等 Web 应用功能的支持.其中,最重要的一个便是对多线程的支持.在 HTML5 中提出了工作线程(Web Worker)的概念,并且规范出 Web Worker 的三大主要特征:能够长时间运行(响应),理想的启

关于Web端即JS端编程

主要的技术是 HTML/JS/CSS/XML Web就是JS/DOM编程. 页面的数据来源: XML, JSON, HTML, Text, 第三方页面或者数据. 不一定都要跟服务器进行交互. JS端 vs Android端 vs IOS端 JS端可以从任何服务器Request数据. JS Request----------->Server1, Server2, Server3获取数据. Android Request------->Server1, Server2, Server3 IOS R

web socket and web worker 基础原理及使用

个人认为HTML5最吸引人的两大功能, web socket 和 worker为构建高效能的web应用提供了新的参考方案. 大体来说,web socket提供更高效的传输协议,web worker提供多线程提高web应用计算效率.最近项目有用到,对应两个问题的解决,目前运行效果来看还是很不错. 这里主要是总结这两个技术的基础原理,和常用API.备忘,也列举关键掌握点,入门和基础使用足以. Web Socket websocket是一种协议,本质上和http,tcp一样.协议是用来说明数据是如何传

基于 HTML5 的 Web SCADA 报表

背景 最近在一个 SCADA 项目中遇到了在 Web 页面中展示设备报表的需求.一个完整的报表,一般包含了筛选操作区.表格.Chart.展板等多种元素,而其中的数据表格是最常用的控件.在以往的工业项目中,所有的表格看起来千篇一律,就是通过数字和简单的背景颜色变化来展示相关信息.但是现在通过各种移动 App 和 Web 应用的熏陶,人们的审美和要求都在不断提高,尤其是在 Web 项目中,还采用老式的数字表格确实也有点落伍了. 如何选择一个合适的 HTML 前端表格控件?此处可以省略一万字.哈哈.j

Web worker 与JS中异步编程的对比

0.从一道题说起 var t = true; setTimeout(function(){ t = false; }, 1000); while(t){ } alert('end'); 问,以上代码何时alert"end"呢? 测试一下:答案是:永远都不会alert. 解析:JavaScript引擎是单线程的,事件触发排队等候.所有任务按照触发时间先后排队处理. 上例中,排队的顺序状态是: | var t=true ; | while(t){}; | alert('end'); | 在