JS线程模型&Web Worker

js线程模型


客户端javascript是单线程,浏览器无法同时运行两个事件处理程序

设计为单线程的理论是,客户端的javascript函数必须不能运行太长时间,否则会导致web浏览器无法对用户输入做出响应。这也是为什么Ajax的API都是异步的,以及为什么客户端Javascript不能使用一个简单的异步load()或者require()函数来加载javascript库

如果应用程序不得不执行太多的计算而导致明显的延迟,应该允许文档在执行这个计算之前完全载入,并且确保告诉用户正在进行计算并且浏览器没有挂起。如果可能应该将任务分散为离散的子任务,可以使用setTimeout()和setInterval()方法在后台运行子任务,同时更新一个进度指示器向用户显示反馈

Web worker简介


HTML5定义了一种作为后台线程的WebWorker。 web worker是一个用来执行计算密集任务而不冻结用户界面的后台线程。

Web worker无法访问window对象和document对象,和主线程之间的通信也只能通过异步消息传递机制来实现。

Web worker 本身不是轻量级的线程,因而常见一些worker去处理次要的操作是不划算的

浏览器支持情况:

包含两部分:

  1. Worker对象:暴露给创建该线程的线程

  2.  WorkerGlobalScope:用来表示新创建的worker的全局对象

Web Worker基本使用


创建新的Worker:

var worker= new Worker("/assets/demo.js");

传递参数:

worker.postMessage("file.text");

接收消息:

worker.onmessage = function(e){
   var message = e.data;
   ......    
}

Worker当然也支持addEventListener()方法和removeEventListener()方法,如果需要管理多个事件时可以使用哒

异常处理:

worker.onerror = function(e){
  console.log("Error at " + e.filename + ":" +e.lineno + e.message );
}

结束Worker

worker.terminate();

载入类和工具函数:

importScripts("utils/base64.js","utils/Map.js"....);

注意:importScripts是同步的方法,一旦importScripts方法返回就可以开始使用载入的脚本,不需要回调函数

Worker作用域


当创建一个新的Worker时该代码会运行在一个全新的Javascript运行环境中(WorkerGlobalScope),完全与创建Worker的脚本隔离

WorkerGlobalScope是Worker的全局对象,因而它包含所有核心Javascript全局对象拥有的属性如JSON等,window的一些属性如self等,也拥有类似XMLHttpRequest()函数

下面简单概括下worker所支持的属性和方法:

self

setTimeout、clearTimeout、setInterval、clearInterval

location

navigator

onerror

XMLHttpRequest

addEventListener、removeEventListener

简单例子


eg1:

html:

<div id="div">
        <p>
            计数:
            <output id="result"></output>
        </p>
        <button onclick="startWorker()">开始 Worker</button>
        <button onclick="stopWorker()">停止 Worker</button>
        
        <br />
        <br />
        <button onclick="mainWork()"> click me </button>
        <br />
        <br />
    </div>

js脚本:

    var w;

        function startWorker() {
            if (typeof (Worker) !== "undefined") {
                if (typeof (w) == "undefined") {
                    w = new Worker("demo.js");
                }
                w.onmessage = function(event) {
                    document.getElementById("result").innerHTML = event.data;
                };
            } else {
                document.getElementById("result").innerHTML = "Sorry, your browser does not support Web Workers...";
            }
        }

        function stopWorker() {
            w.terminate();
        }
        
        function addE(){
            var p = document.createElement('p');
            p.innerHTML="WISH YOU HAPPY~";
            div.appendChild(p);
        }

demo.js:

/**
 * Web Worker Demo----count
 * 
 */
 var i=0;

function timedCount()
{
i=i+1;
postMessage(i);
setTimeout("timedCount()",500);
}

timedCount();

结果:

点击开始Worker后,计数在后台进行,可以点击click me,相互不影响


 eg2:

如上面所说,我们应该尽量使用WebWorker处理计算量大的,主要的工作,否则因为WebWorker本身不是轻量级的线程,因而有点得不偿失

本例使用WebWoker处理图片,将图片模糊,顺便学习下canvas,如下:

html:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
img {
      width: 400px;
      height: 300px;
    }
</style>
</head>
<body>
<img src="../images/demo.png" onclick="smear(this)"/>
<img src="../images/1.png" onclick="smear(this)"/>
<img src="../images/2.png" onclick="smear(this)"/>
<script>
    function smear(img){
        var canvas = document.createElement("canvas");
        canvas.width = img.width;
        canvas.height = img.height;
        
        var context = canvas.getContext("2d");
        context.drawImage(img,0,0);
        var pixels = context.getImageData(0,0,img.width,img.height);
        
        var worker = new Worker("SmearWorker.js");
        worker.postMessage(pixels);
        
        worker.onmessage = function(e){
            var smeared_pixels = e.data;
            context.putImageData(smeared_pixels,0,0);
            img.src = canvas.toDataURL();
            worker.terminate();
            canvas.width = canvas.height = 0;
        }
    }
</script>

</body>
</html>

 SmearWorker.js:

/**
 * SmearWorker---smear the picture
 */
 
 function smear(pixels){
     var data = pixels.data,
         width = pixels.width,
         height = pixels.height;
     
     var n = 10,
         m = n-1,
         i,
         col;
         
     for(var row=0; row<height; row++){
         i = row*width*4 + 4;
         for(col =1;col<width; col++,i+=4){
             data[i] = (data[i] +data[i-4]*m/n);
             data[i+1] = (data[i+1] +data[i-3]*m/n);
             data[i+2] = (data[i+2] +data[i-2]*m/n);
             data[i+3] = (data[i+3] +data[i-1]*m/n);
             
         } 
     }
     return pixels;
 }
 
 onmessage = function(e){
     postMessage(smear(e.data));
 };

结果如下:

图片点击前:

点击第一张和第三张图片:

 

其他


我们在js中使用XMLHttpRequest时经常会设置为异步方式,因而在主浏览器线程张使用同步很不好,我们可以在worker中使用同步的XMLHttpRequest

原文地址:https://www.cnblogs.com/wishyouhappy/p/3766225.html