Web Worker

Web Worker

在web worker规范产生之前,dom渲染和javascript代码执行是在同一个浏览器线程中执行的。也就是说:渲染dom的时候不能执行javascript代码,执行javascript代码的时候,UI界面会暂停响应。如果javascript代码执行时间很长,那么UI就会无响应,这就是所谓的页面卡死。Web Workers是 HTML5 提供的一个javascript多线程解决方案,我们可以将一些耗时的javascript代码交由web Worker运行而不冻结用户界面。也就是说web workerUI界面是运行在不同线程中的。

创建Web Worker文件

//my_worker.js  
  
//这里this并不是传统意义上的window对象,而是一个WorkGlobalScope对象  
var self = this;  
  
  
// worker入口,类似于Thread的run方法  
self.onmessage = function(evt){  
      
    // 接收传递过来的参数  
    var millSeconds = evt.data;  
      
    wait(millSeconds);  
      
    console.log("work time is: " + new Date());  
      
    // 返回数据给调用者  
    postMessage("from worker's message.");  
}  
  
  
function wait(millSeconds)  
{  
    var begin = new Date().getTime();  
    var end = new Date().getTime();  
    while(end - begin < millSeconds)  
    {  
        end = new Date().getTime();  
    }  
}  
worker新线程使用通过postMessage和onmessage方法:
1.通过postMessage( data ) 方法来向主线程发送数据。
2.绑定onmessage方法来接收主线程发送过来的数据。

创建Web Worker对象

<!DOCTYPE HTML>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <script type="text/javascript">
        // 开启一个web worker线程(加载js文件,创建worker对象)
        var worker =new Worker("my_worker.js"); 
        
        // 传递数据给work
        worker.postMessage(5000);     
        
        console.log("continue ...");
        
        // 接收work返回的数据
        worker.onmessage =function(evt){    
            console.log("main now is: " + new Date());            
            console.log(evt.data);              
        }
        
        console.log("no wait ...");
        
    </script>
 </head>
<body></body>
</html>
我们的main.htm(WEB主线程)主要做以下事情:
1.通过 worker = new Worker( url ) 加载一个JS文件来创建一个worker,同时返回一个worker实例。
2.通过worker.postMessage( data ) 方法来向worker发送数据。
3.绑定worker.onmessage方法来接收worker发送过来的数据。
4.使用 worker.terminate() 来终止一个worker,释放占用的资源。

运行含Web Worker的页面

上面的main.html页面使用了Web Work,必需将main.html和my_worker.js部署到web容器(比如Tomcat)下才能运行。如果直接在本地运行main.html页面,会报错:

Uncaught DOMException: Failed to construct 'Worker': Script at 'file:///D:/learn/html/test/demo_workers.js' cannot be accessed from origin 'null'.

Web Worker的跨域访问限制

在main.html中通过new Work(url)来创建work对象,加载的js文件不能跨域。比如我们将main.html放在端口是8080的Tomcat下,将my_worker.js放在端口是9090的Tomcat下,那么main.html是不能使用my_work.js的。

var worker =new Worker("http://localhost:9090/demo/my_worker.js");   
/* 
Uncaught SecurityError: Failed to construct 'Worker':  
    Script at 'http://localhost:9090/demo/my_worker.js'  
    cannot be accessed from origin 'http://localhost:8080'. 
*/  

Web Worker的异常处理和终止

WEB主线程加载web worker一般没有什么异常,不过也有2种情况会导致创建web work报错,比如我们跨域访问web worker文件,再比如web work中的js代码出现了未捕获的异常。如果main.html需要关注这些异常,那么我们可以使用onerror回调:

var worker =new Worker("my_worker.js");   
    
worker.onerror = function(e){  
    console.log("load web worker error."+e);  
};  

当main.html不再需要使用web worker的时候,需要调用worker对象的terminate()方法来释放web worker占用的资源。

worker.terminate();

web worker中使用importScripts加载外部js

我们知道在html页面中,可以使用<script>标签加载外部的js文件,而且script标签还支持跨域加载js。如果我们的web worker中也需要访问外部的js文件,那么可以使用importScripts函数来加载文件,而且也是跨域的。

//my_worker.js  
  
  
var self  =this;  
importScripts("http://localhost:9090/demo/util.js");  
importScripts("math.js");  
  
onmessage = function(evt){  
      
    var millSeconds = evt.data;  
      
    wait(millSeconds);  
      
    console.log("work time is: " + new Date());  
    postMessage("from worker's message.");  
      
    warn("sss");//调用util.js中的warn函数  
}  
  
  
function wait(millSeconds)  
{  
    var begin = new Date().getTime();  
    var end = new Date().getTime();  
    while(end - begin < millSeconds)  
    {  
        end = new Date().getTime();  
    }  
}  

importScripts使用方式如下:

importScripts('script1.js');  
importScripts('script2.js');  
  
//Which can also be written as a single import statement:  
importScripts('script3.js', 'script4.js');  

Web Work的局限性

在web worker的js中无法访问main.html中的DOM对象、window对象、document对象等,这是属于功能上的局限性。还有不同厂家的浏览器对Web Worker规范的实现并不一致,导致运行效果的差异。这篇文章介绍了web worker中一些API的不兼容性。所以使用web worker需要小心,不然会导致功能性问题和兼容性问题。

使用Blob URL实现Web Worker的inline

一般来说web worker都是放在一个单独的外部js文件中的,这就是最佳实践。如果只是为了自己平时测试需要,将web worker和main.html放在一起也是可以的。需要借助window.URL.createObjectURL()来实现inline。比如下面的main.html是可以正常运行的。

<!DOCTYPE HTML>  
<html>  
<head>  
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>  
    <script type="text/javascript">  
    var blob = new Blob(["onmessage = function(e) { postMessage('msg from worker'); }"]);  
  
    // Obtain a blob URL reference to our worker 'file'.  
    var blobURL = window.URL.createObjectURL(blob);  
  
    var worker = new Worker(blobURL);  
    worker.onmessage = function(e) {  
        console.log(e.data);  
    };  
      
    worker.postMessage(1); // Start the worker.  
          
    </script>  
 </head>  
<body></body>  
</html>  

 

posted @ 2017-06-04 15:33  zhoulixue  阅读(1186)  评论(0编辑  收藏  举报