1. 程式人生 > >深入理解javascript非同步程式設計障眼法&&h5 web worker實現多執行緒

深入理解javascript非同步程式設計障眼法&&h5 web worker實現多執行緒

0.從一道題說起

var t = true; 
setTimeout(function(){ t = false; }, 1000); 
while(t){ }
alert('end');

問,以上程式碼何時alert“end”呢?
測試一下:答案是:永遠都不會alert。

解析:javascript引擎是單執行緒的,事件觸發排隊等候。所有任務按照觸發時間先後排隊處理。
上例中,排隊的順序狀態是:

| var t=true ; | while(t){}; | alert(‘end’); |

在1000ms之後,setTimeout函式也加入佇列。
while(t){ }無限迴圈阻塞了單執行緒,不管排到後面的程式碼執行時間有多短,後面的程式碼無法執行,一直阻塞下去。

1.瀏覽器執行緒

瀏覽器有這麼幾大執行緒:UI渲染執行緒(用於頁面的渲染),javascript引擎執行緒(用於處理js),GUI事件觸發執行緒(用於互動)。

有時會開啟的執行緒:http傳輸執行緒定時觸發執行緒(定時器)

它們之間的關係是什麼呢?

(1)UI渲染執行緒 與 javascript引擎執行緒 互斥

由於javascript可以操縱頁面的DOM,所以如果UI渲染執行緒與javascript引擎執行緒 不互斥的話,在UI渲染執行緒進行頁面渲染的同時,javascript引擎執行緒進行DOM修改,最終會造成DOM狀態不一致的現象。所以,當javascript引擎執行緒執行的時候,UI渲染執行緒處於凍結狀態。

(2)javascript引擎執行緒 與 GUI事件觸發執行緒(用於互動) 非同步

瀏覽器開啟事件觸發執行緒,等待使用者動作,事件觸發執行緒解析為響應事件,轉移到javascript引擎執行緒,排隊等候,等待javascript引擎的處理。

(3)javascript引擎執行緒 與 http傳輸執行緒 非同步

網頁get,post等請求,xhr非同步請求都通過http傳輸執行緒,傳送到javascript引擎排隊,等候處理。

(4)javascript引擎執行緒 與 定時觸發執行緒(定時器) 非同步

setTimeout(),setInterval()由單獨的執行緒 定時觸發執行緒 觸發,傳送到javascript引擎排隊等候,等待處理。

2.xhr非同步是障眼法

我們來做一個試驗:

客戶端js程式碼

//jquery封裝的ajax請求,請求http://localhost:3000/login頁面
 $.ajax({
        type: "post",
        url: "http://localhost:3000/login",
        dataType: "json",
        data:{ username: username, password: password },
        success: function(data){
            if(data){
               if(data.message=="i202"){
                   alert('密碼錯誤,請重新輸入');
                   window.location.href="login";
               }else if(data.message=="i200"){
                    alert('登陸成功');
                    window.location.href="index";
                }
                else{
                    alert('沒有這個使用者名稱');
                   window.location.href="login";
                 }
            } else{


            }
        }
    });
    //這裡有一個無限迴圈
    while(1){}

後端node.js程式碼:

//後臺對post的響應
router.post('/login', function (req, res, next) {    
    sleep(1000);
    res.send({status:"success", message:"i200"});     
});

/**
 * 模擬sleep
 * @param sleepTime
 */
function sleep(sleepTime) {
    for(var start = +new Date; +new Date - start <= sleepTime; ) { }
}

前臺將永遠不會alert(“登陸成功”)。瀏覽器通過http執行緒收到xhr響應,但是轉到javascript執行緒等待執行。javascript單執行緒,一次只能處理一個任務。第一個任務無限迴圈,後面的任務全部阻塞。

xhr非同步程式設計實際上是一種障眼法。

3.定時器時間不準

(1)時間不準1

 setTimeout(function () { while (true) { } }, 1000);
        setTimeout(function () { alert('end 2'); }, 2000);
        setTimeout(function () { alert('end 1'); }, 100);
        alert('end');

執行這段程式碼。執行結果是alert(‘end’) alert(‘end 1’)。

前兩個定時器並不能如約在規定的時間點執行哦。

(2)時間不準2

setTimeout(function(){  
   /* 程式碼塊... */  
   setTimeout(arguments.callee, 10);  
}, 10);  

setInterval(function(){  
   /*程式碼塊... */  
 }, 10); 

兩個定時器,本想實現相同的功能:每十秒觸發一次定時器。

但是實際上,setTimeout在10ms後才加入js執行佇列,排隊等待。所以每兩次定時器觸發的時間間隔可能 > 10ms。

setInterval每10s就向js執行佇列新增一個setInterval事件等待執行。前面的setInterval事件可能被它之前的事件阻塞,導致執行晚了幾拍。那麼沒兩次定時時間觸發的時間間隔可能 <10ms 。

3.web worker 才是真正多執行緒

來吧,試驗一下:

index.html

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<script src="js/Fthread.js"></script>
</body>
</html>

Fthread.js

//這裡建立一個webworker就是開一個新的執行緒
var worker=new Worker('js/Sthread.js');//建立子執行緒
//這裡接收新的執行緒傳來的data
worker.onmessage = function(event) {
    console.log(event.data);
};
//這個將會觸發向子程序的請求
worker.postMessage("begin");

//構造一個無限迴圈
setTimeout(function () { while (true) { } }, 1000);

Sthread.js

//這裡佔有一個新執行緒,向主執行緒傳送訊息
postMessage('hello');

//實現之前的一個例項,看是否阻塞
setTimeout(function () { console.log('end 2'); }, 2000);
setTimeout(function () { console.log('end 1'); }, 100);
console.log('end');

執行結果:

end
hello  //這是兩個執行緒資料的傳送,可以不看
end1

哈哈,end1沒有被阻塞,因為人家是在子執行緒執行滴。這才是多執行緒嘛。

看到某大大在部落格裡說可以使用webworker開執行緒給後臺傳送實時心跳。告訴後臺前臺沒崩。不過開一個執行緒據說開銷很大。這就。。。