標籤:== html arguments 主線程 cal 代碼執行 響應 入隊 導致
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‘);//建立子線程//這裡接收新的線程傳來的dataworker.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‘);
運行結果:
endhello //這是兩個線程資料的傳送,可以不看end1end2
hello沒有被定時器非同步阻塞,因為人家是在子線程運行滴。這才是多線程嘛。JS中的非同步雖然也有開線程進行處理相關的操作,但是其回呼函數還是需要通過主線程輪詢執行,因而並不是完全的多線程。webworker是基於瀏覽器引擎的
轉載請註明出處;http://www.cnblogs.com/heshan1992/p/6698069.html
Web worker 與JS中非同步編程的對比