首頁  >  文章  >  web前端  >  HTML5新特性之多執行緒(Worker SharedWorker)的程式碼詳解

HTML5新特性之多執行緒(Worker SharedWorker)的程式碼詳解

黄舟
黄舟原創
2017-04-24 10:30:121813瀏覽

  There is no doubt that JavaScript是沒有多線程之說的,他只能一件事一件事的做,做完一件事再做下一件事,假如你的js要花一段比較長的時間做一件事的話,那麼瀏覽器將會卡頓一段時間,不對用戶的操作產生回應,這可咋辦呢?謝天謝地,HTML5為我們提供了實現多線程的機制,這麼好的東西,想必你早就再用了,不過沒關係啊,咱們一塊兒複習一下咯!

一、Worker類別

  1、方法介紹

  (1)建構子 new Worker(arg)  :參數表示你的執行緒要執行的程式碼所在的js文件,例如'myworker.js',建構子當然是回傳一個Worker類別的實例

  (2)worker.postMessage(message):這個方法表示從主執行緒向子執行緒傳送訊息或子執行緒向主執行緒發送訊息,message一般是一個字串,也可以將一個js物件轉成字符串發過去

  (3)worker上還有一個message事件,當有人向這個worker實例發送訊息時,該事件被觸發,我們可以從他的事件物件的data 屬性中獲得post過來的值

  可以看到Woker類別的API是相當簡潔的,只有兩個最常用的方法,一個事件,下面我們來透過實際的例子看看。

//main.html
<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>main</title></head><body>
    <p id="out"></p>
    <input type="text" name="" id="txt">
    <button id="btn">发送</button>
    <script type="text/javascript">
        var out = document.getElementById("out");        
        var btn = document.getElementById("btn");        
        var txt = document.getElementById("txt");        
        var worker = new Worker("thread1.js");
        btn.addEventListener("click",function(){            
        var postData = txt.value;
            worker.postMessage(postData);
        },false);
        worker.addEventListener(&#39;message&#39;,function(e){
            out.innerText = e.data;
        },false);    </script></body></html>
//thread1.js
onmessage = function(event){    
var res = event.data+"帅气!";
    postMessage(res);
}

  當我在文字方塊輸入「大~熊」點擊發送按鈕就會出現如下效果

  

#  簡單分析分析,我在主執行緒由thead1.js創建了一個Worker的實例worker,當點擊按鈕時會呼叫他的postMessage方法,將文字方塊中的內容傳送到thread1.js,我們的thread1.js怎麼做的呢?是這樣,他偵聽message事件,主線程發送消息過來就觸發這個事件,執行回調函數,回調函數從事件對象取得發送來的值,然後將這個值加上“帥氣!” ,然後在發送回去。主執行緒呢也偵聽了worker的message事件,所以有訊息過去式會觸發,將訊息內容顯示在p中,如此就看到了上面的效果。

  或許你會將這有什麼用呢?這裡確實沒什麼用,這裡我們大可以在主線程還總完成加“帥氣!”的操作,因為他的複雜度為O(1)(哈哈,最近在學算法!),但是假如不是做這麼簡單的操作呢?這種方法的好處就是不過你的子執行緒做多麼複雜的工作,都不會讓主執行緒停下來,主執行緒改幹嘛還幹嘛,等到子執行緒把資料處理好了他直接拿過來就好了。

  陸老師將可以在子線程中在調用new Worker()創建新的子線程,我發現這樣是不可以的,會報undefined錯誤,也就是說子線程中是不能調用Worker構造函數的,一開始以為是自己錯了,後來查閱了官方文檔,發現也沒有相關的描述。

  下面看一個在主線程調用多個子線程的例子:

//main.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>main</title>
</head>
<body>
    <div id="out"></div>
    <input type="text" name="" id="txt">
    <button id="btn">发送</button>
    <script type="text/javascript">

        var out = document.getElementById("out");
        var btn = document.getElementById("btn");
        var txt = document.getElementById("txt");
        var worker1 = new Worker("thread1.js");
        var worker2 = new Worker("thread2.js");
        btn.addEventListener("click",function(){
            var postData = txt.value;
            worker1.postMessage(postData);
        },false);
        worker1.addEventListener(&#39;message&#39;,function(e){
            worker2.postMessage(e.data);
            
        },false);
        worker2.addEventListener(&#39;message&#39;,function(e){
            out.innerText = e.data;
        },false);
    </script>
</body>
</html>
//thread1.js
onmessage = function(event){    
var res = event.data+"帅气!";
        postMessage(res);    
}
//thread2.js
onmessage = function(event){    
var res = event.data+"没骗你哟!";
    postMessage(res);
    close();
}

  主線程要完成一個任務需要兩個線程,它創建了兩個線程worker1,2,先向worker1請求,得到回傳的資料後,再請求worker2,同時將worker1處理之後的資料交給worder2處理,然後拿到最終結果,顯示在頁面上。

  在子執行緒中可以引入其他的js檔案然後調用,例如下邊這個例子。

//main.html
<!DOCTYPE html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>main</title></head><body>
    <p id="out"></p>
    <input type="text" name="" id="txt">
    <button id="btn">发送</button>
    <script type="text/javascript">

        var out = document.getElementById("out");        
        var btn = document.getElementById("btn");        
        var txt = document.getElementById("txt");        
        var worker1 = new Worker("thread1.js");
        btn.addEventListener("click",function(){            
        var postData = txt.value;
            worker1.postMessage(postData);
        },false);
        worker1.addEventListener(&#39;message&#39;,function(e){
            out.innerText = e.data;
            
        },false);    </script></body></html>
//thread1.js
importScripts(&#39;tools.js&#39;)
onmessage = function(event){
    var res = handler(event.data);
        postMessage(res);    
}
//tools.js
function handler(data){
    return data+"加油加油!"
}

  可以看到我們的thread1.js並沒有一個叫做tools.js的文件,但是它透過importScripts()導入了一個js文件,然後就可以呼叫裡邊暴露出來的方法了。

二、SharedWorker類別

  SharedWorker的實質在於share,不同的線程可以共享一個線程,他們的資料也是共享的。

  直接用例子來探討。

  使用方法一:

//main.html
<!DOCTYPE HTML><head>
    <title>Shared workers: demo 1</title></head><body>
    <p id="log"></p><script>
  var worker = new SharedWorker(&#39;shared.js&#39;);  
  var log = document.getElementById(&#39;log&#39;);
  worker.port.onmessage = function(e) { 
  // note: not worker.onmessage!    
  log.textContent += &#39;\n&#39; + e.data;
  }</script></body></html>
//shared.js

onconnect = function(e) {
  var port = e.ports[0];
  port.postMessage(&#39;Hello World!&#39;);
}

  這是從w3c拿得一個例子,下面先看第二種方法,再做分析

<!DOCTYPE HTML>
<html>
<head>
    <title>Shared workers: demo 2</title>
    </head>
    <body>
    <p id="log"></p>
    <script>
  var worker = new SharedWorker(&#39;shared.js&#39;);  
  var log = document.getElementById(&#39;log&#39;);
  worker.port.addEventListener(&#39;message&#39;, function(e) {
    log.textContent += &#39;\n&#39; + e.data;
  }, false);
  worker.port.start(); // note: need this when using addEventListener  
  worker.port.postMessage(&#39;ping&#39;);</script>
  </body></html>  
//shared
onconnect = function(e) {
  var port = e.ports[0];
  port.postMessage(&#39;Hello World!&#39;);
  port.onmessage = function(e) {
    port.postMessage(&#39;pong&#39;); // not e.ports[0].postMessage!
    // e.target.postMessage(&#39;pong&#39;); would work also
  }
}

  第一种方法中是使用事件句柄的方式将听message事件,不需要调用worker.port.start(),第二种方法是通过addEventListener()方法监听message事件,需要worker.port.start()方法激活端口。他们不同于worker,当有人和他通信时触发connect事件,然后他的message事件是绑定在messagePort对象上的,不想worker那样,你可以回头看看worker是怎么做的。

  那么sharedWorker是怎么共享数据的呢?请看下面的例子。

//main1 和main2都是这样
<!DOCTYPE HTML>
<html>
<head>
    <title>Shared workers: demo 2</title>
    </head>
    <body>
    <p id="log"></p>
    <input type="text" name="" id="txt">
    <button id="get">get</button>
    <button id="set">set</button>
    <script>
  var worker = new SharedWorker(&#39;shared.js&#39;);  
  var get = document.getElementById(&#39;get&#39;);  
  var set = document.getElementById(&#39;set&#39;);  
  var txt = document.getElementById(&#39;txt&#39;);  
  var log = document.getElementById(&#39;log&#39;);

  worker.port.addEventListener(&#39;message&#39;, function(e) {
    log.innerText = e.data;
  }, false);
  worker.port.start(); // note: need this when using addEventListener
  set.addEventListener(&#39;click&#39;,function(e){
      worker.port.postMessage(txt.value);
  },false);

  get.addEventListener(&#39;click&#39;,function(e){
      worker.port.postMessage(&#39;get&#39;);
  },false);</script></body></html>
//shared
var data;
onconnect = function(e) {
  var port = e.ports[0];
  port.onmessage = function(e) {
    
    if(e.data==&#39;get&#39;){
        port.postMessage(data);
    }else{
        data=e.data;
    }
  }
}

  这里分析一波,我们在main1.html的文本框输入数据,点击set,然后在main2.html中点击get法现能够获取到我们在main1.html中设置的数据,这说明我们的sharedWorker事实上是单例的,就像java中的static类一样,不管new多少个,实际上只有一个,这样我们的不同线程就可以共享到sharedWorker中的数据了。这里把图给上,记得有篇文章没给图,然后有人给我建议了,问能不能给图。

  

  最后来小结一下,worker和sharedWorker没有什么悬糊的,就是把台前的工作搬到幕后去做,不打断台前的工作。正所谓台上十分钟,台下十年功,如果你把台下的十年供放到台上做,观众的唾沫星子早就把你淹死了,所以说那些费事费力的工作还是放到台下去,台上只用展示你最好的一面的好了,十分钟足以! 

以上是HTML5新特性之多執行緒(Worker SharedWorker)的程式碼詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn