灏天阁

多线程处理

· Yin灏

Web Worker 简介

我们都知道,JavaScript 的执行环境是单线程的。所谓的“单线程”,指的是一次只能执行一个任务。如果有多个任务,就必须排队,后面的任务必须等前面的任务执行完成后才能执行。

单线程这种方式有一个很大的缺点,就是如果前面有一个耗时很长的任务,后面所有任务都必须等待它完成后才能执行。我们经常看到浏览器没有响应(即假死),往往就是因为某一段 JavaScript 代码长时间运行(比如死循环),导致后面的任务无法执行。

在 HTML5 中,我们可以使用 Web Worke r创建一个“后台线程”来执行某一段耗时较长的 JavaScript 程序,而不会影响页面响应。Web Worker 其实就是 HTML5 提供的 “JavaScript多线程” 解决方案。

Web Worker 技术基本原理就是:在当前 JavaScript 的主线程中,使用 Worker() 构造函数新建一个 worker 实例,然后加载某一个 JavaScript 文件,发送给一个后台线程来处理(注意,这里是后台线程)。

//新建worker实例
var worker = new Worker(url);
//向后台发送数据
worker.postMessage(yourdata);
//接收后台处理完成的数据
worker.onmessage = function(e){
  //e.data
};

想要使用 Web Worker,首先我们需要使用 Worker() 构造函数新建一个 worker 实例,其中,参数 url 表示需要发送到后台线程处理的 JavaScript 文件的路径。

worker.postMessage() 表示发送数据给 worker 线程,其中参数 yourdata 可以是数字、字符串、对象等。

worker.onmessage = function(e){}; 表示接收worker发过来的数据,然后进行处理。在处理函数内部,我们可以使用 e.data 来获取发过来的数据。

特别注意一点,Web Worker 必须依赖服务器环境,毕竟是开辟了一个“后台线程”嘛。也就是说,想要使用 Web Worker,我们必须先要搭建服务器环境。

在下面的例子中,我们在服务器 “www” 文件夹下建立一个名叫 “worker” 的文件夹,然后在新建好的 “worker” 文件夹中再新建两个文件:worker.html、worker.js。

worker.html 代码如下:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <title></title>
  <script>
    var worker = new Worker("worker.js");
    //前台向后台发送数据
    worker.postMessage("绿叶学习网");
    //前台接收后台发来的数据
    worker.onmessage = function(e){
      console.log(e.data);
    };
  </script>
</head>
<body>
</body>
</html>

worker.js 代码如下:

onmessage = function(e){
  //通过e.data获取前台发送来的数据
  var d = e.data;
  var str = d.split("").reverse().join("");
  postMessage(str);
};

开启 WampServer,然后在浏览器地址栏输入 http://localhost/worker/worker.html,此时浏览器控制台输出结果。

分析:

在这个例子中,worker.html 是在前台处理的,而 worker.js 是发送到后台处理的。

var worker = new Worker("worker.js");
worker.postMessage("绿叶学习网");
worker.onmessage = function(e){
  console.log(e.data);
};

对于 worker.html 这段代码,首先我们使用 Worker() 构造函数来新建一个 worker 实例,然后使用 postMessage() 方法向后台发送数据。特别要注意一点,postMessage() 方法执行之后,此时就跳到后台的 worker.js 进行数据处理,等后台 worker.js 处理完成,前台 worker.html中 的 worker.onmessage = function(e){}; 才能接收数据。

onmessage = function(e){
  //通过e.data获取前台发送来的数据
  var d = e.data;
  //数据处理
  var str = d.split("").reverse().join("");
  postMessage(str);
};

对于 worker.js 这段代码,格式是固定下来的,都是使用 onmessage = function(e){};来处理前台发送过来的数据,处理完成后再使用 postMessage() 方法向前台返回处理完成后的数据。

Web Worker 应用

“Web Worker 这个技术到底有什么用?它可以帮我们解决哪些问题呢?”其实,Web Worker 最重要的用途就是使用多线程的方式来处理耗时较长的 JavaScript 程序。

举一个简单的例子,著名的 fibonacci 数列是比较经典的递归数学公式:

用文字来描述就是:fibonacci 数列是由 0 和 1开始,之后每一个数都是前两个数相加之和。从上面规律可以知道,前几个 fibonacci 数是:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233

如果用 JavaScript 求第 n 个 fibonacci 数,实现代码如下:

var fibonacci =function(n) {
  return n<2 ? n : arguments.callee(n-1) + arguments.callee(n-2);
};

众所周知,递归是非常耗时的。如果在 Chrome 浏览器中计算 39 的 fibonacc i数列,执行时间约为 19 秒。而当计算 40 的 fibonacci 数列时,浏览器直接提示脚本忙了。这个时候,最好的办法就是使用Web Worker 开启后台线程来处理。

在下面例子中,我们在服务器中 “worker” 这个目录下再新建两个文件:fibonacci.html、fibonacci.js。

fibonacci.html 代码如下:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title></title>
    <script>
        var worker = new Worker("fibonacci.js");
        //10
        worker.postMessage(10);
        worker.onmessage = function (e) {
            console.log(e.data);
        };
        //20
        worker.postMessage(20);
        worker.onmessage = function (e) {
            console.log(e.data);
        };
        //30
        worker.postMessage(30);
        worker.onmessage = function (e) {
            console.log(e.data);
        };
    </script>
</head>

<body>
</body>

</html>

fibonacci.js 代码如下:

var fibonacci = function (n) {
  return n < 2 ? n : arguments.callee(n - 1) + arguments.callee(n - 2);
};
onmessage = function (e) {
  var result = fibonacci(e.data);
  postMessage(result);
};

分析:

对于Web Worker,最后还有以下 3 点:

  • Web Worker 由于使用的是后台线程,发送给后台线程的那个 JavaScript 文件的使用有一定的限制,例如无法访问 DOM、无法访问全局变量或全局函数等。
  • Web Worker 依然可以使用定时器的4个函数:setTimeout()、clearTimeout()、setInterval()、clearInterval()。
  • Web Worker 不支持跨域加载JavaScript。

实战题:后台计算

在 Web 应用中,我们应该把非即时性耗时过长的任务放在后台处理,以减轻前台处理的压力。

在下面例子中,我们在前台页面中随机生成一个整数的数组,然后把这个数组传到后台去处理,让后台挑选出数组中可以同时被 3 和 5 整除的数字,最后再在前台页面中输出。

首先,我们在服务器中 “worker” 目录下新建两个文件:calculate.html、calculate.js。

calculate.html 代码如下:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title></title>
    <script>
        //随机生成包含200个元素的数组
        var arr = [];
        for (var i = 0; i < 200; i++) {
            arr[i] = Math.floor(Math.random() * (200 + 1));
        }
        var worker = new Worker("calculate.js");
        //前台向后台发送数据
        worker.postMessage(arr);
        //前台接收后台发来的数据
        worker.onmessage = function (e) {
            console.log(e.data);
        };
    </script>
</head>

<body>
</body>

</html>

calculate.js 代码如下:

onmessage = function (e) {
  var arr=e.data;
  var result="";
  for(var i=0;i<200;i++){
    if((arr[i]%3==0)&&(arr[i]%5==0)){
      result +=arr[i]+",";
    }
  }
  postMessage(result);
};

分析:

Math.floor(Math.random()*(200+1)) 表示随机生成一个 0 到 200 之间的整数。

- Book Lists -