Ⅰ 載入js時,很容易導致js的堵塞,該怎麼處理
無阻塞載入js
瀏覽器載入靜態資源和js的方式都是線性載入,所以一般情況可以將js放到</body>前,防止UI線程的阻塞。
而某些時候我們既希望js在整個網頁的頭部就載入,又擔心js阻塞導致網站載入緩慢,就可以用到無阻塞載入js技術。
Dynamic Script Elements 動態腳本元素
DOM允許我們使用javascript動態創建HTML的幾乎所有文檔內容,一個新的<script>元素可以非常容易的通過標准DOM創建:
var script = document.createElement ("script"); script.type = "text/javascript"; script.src = "file1.js"; document.body.appendChild(script);
新的<script>元素載入file1.js源文件。此文件當元素添加到頁面後立刻開始下載。此技術的重點在於:無論在何處啟動下載,文件的下載和運行都不會阻塞其他頁面處理過程。
當文件使用動態腳本節點下載時,返回的代碼通常立即執行(除了Firefox和Opera,它們將等待此前的所有動態腳本節點執行完畢)。
大多數情況下,我們希望調用一個函數就可以實現Javascript文件的動態下載。下面的函數封裝實現了標准實現和IE實現:
function loadScript(url, callback){ var script = document.createElement ("script") ; script.type = "text/javascript"; if (script.readyState){ //IE script.onreadystatechange = function(){ if (script.readyState == "loaded" || script.readyState == "complete"){ script.onreadystatechange = null; callback(); } }; } else { //Others script.onload = function(){ callback(); }; } script.src = url; document.getElementsByTagName("head")[0].appendChild(script); } loadScript("file1.js", function(){ //調用 alert("File is loaded!"); });
此函數接受兩個參數:Javascript文件的Url和一個當Javascript接收完成時觸發的回調函數。屬性檢查用於決定監視哪種事件。最後一步src屬性,並將javascript文件添加到head。
動態腳本載入是非阻塞Javascript下載中最常用的模式,因為它可以跨瀏覽器,而且簡單易用。
Ⅱ 如何用node.js模擬多線程
Fibers 又稱纖程,可以理解為協同程序,類似py和都有這樣的模型。使用Fibers可以避免對資源的互搶,減少cpu和內存的消耗,但是Fibers並不能夠真正的並行執行,同一時刻只有一個Fibers在執行,如果在其中一個Fibers中執行過多的cpu操作或者寫了個死循環,則整個主程序將卡死住。node中的非同步事件循環模型就有點象這個。
Threads 又稱線程,他可以在同一時刻並行的執行,他們共享主進程的內存,在其中某一時刻某一個threads鎖死了,是不會影響主線程以及其他線程的執行。但是為了實現這個模型,我們不得不消耗更多的內存和cpu為線程切換的開銷,同時也存在可能多個線程對同一內存單元進行讀寫而造成程序崩潰的問題。
很多讓node支持多線程的方法是使用c/c++的addon來實現,在需要進行cpu密集型計算的地方,把js代碼改寫成c/c++代碼,但是如果開發人員對c++不是很熟悉,一來開發效率會降低不少,二來也容易出bug,而且我們知道在addon中的c++代碼除了編譯出錯外,是很難調試的,畢竟沒有vs調試c++代碼方便。
令人振奮的消息,我們為什麼不讓node也支持多線程模型呢?於是Jorge為我們開發出了一個讓node支持多線程模型的模塊:threads_a_gogo
github地址:https://github.com/xk/node-threads-a-gogo
有了threads-a-gogo(以下簡稱TAGG)這個模塊之後,我們可以讓node做更多的事情,我記得以前我看過一篇文章,說node只能應付i/o密集型場景,在cpu密集型場景將完敗給apache,因為apache是為每一個請求起一條線程的,所以在處理cpu密集型任務時一個線程的高強度計算不會很大程度的影響其他線程,類似的還有php的fastcgi,這也是很多拿node和php進行比較時,php的擁護者們一直提出的理論。
我們先來做一個簡單的測試,用我們suqian大大最喜歡的斐波那契數組來看一下,加入了多線程的node有多麼的強悍:(測試機器為4CPU)
沒有使用TAGG的正常情況,非同步也幫不了我們應對cpu密集型任務
function fibo (n) {
return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
}
var n=8
function back(){
if(!--n) return console.timeEnd('no thread');
}
console.time('no thread');
process.nextTick(function(){
console.log(fibo (40));
back();
})
process.nextTick(function(){
console.log(fibo (40));
back();
})
process.nextTick(function(){
console.log(fibo (40));
back();
})
process.nextTick(function(){
console.log(fibo (40));
back();
})
process.nextTick(function(){
console.log(fibo (40));
back();
})
process.nextTick(function(){
console.log(fibo (40));
back();
})
process.nextTick(function(){
console.log(fibo (40));
back();
})
process.nextTick(function(){
console.log(fibo (40));
back();
})
我們模擬了8個非同步的行為,測試用的node v0.8.16版本,所以 process.nextTick還是非同步方法。最後我們輸出結果為:
165580141
165580141
165580141
165580141
165580141
165580141
165580141
165580141
no thread: 23346ms
接下來我們使用TAGG模塊來測試同樣的執行8次斐波那契數組計算,看看成績如何?
function fibo (n) {
return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
}
console.time('8 thread');
var numThreads= 8; //創建線程池,最大數為8
var threadPool= require('threads_a_gogo').createPool(numThreads).all.eval(fibo); //為線程池注冊程序
var i=8;
var cb = function(err,data){ //注冊線程執行完畢的回調函數
console.log(data);
if(!--i){
threadPool.destroy();
console.timeEnd('8 thread');
}
}
threadPool.any.eval('fibo(40)', cb); //開始向線程池中執行fibo(40)這個任務
threadPool.any.eval('fibo(40)', cb);
threadPool.any.eval('fibo(40)', cb);
threadPool.any.eval('fibo(40)', cb);
threadPool.any.eval('fibo(40)', cb);
threadPool.any.eval('fibo(40)', cb);
threadPool.any.eval('fibo(40)', cb);
threadPool.any.eval('fibo(40)', cb);
最重的結果:
165580141
165580141
165580141
165580141
165580141
165580141
165580141
165580141
8 thread: 9510ms
相比不使用多線程模型的node,使用了TAGG模塊之後,我們在4CPU伺服器上的測試結果要快上一倍還不止。
到這里我們看上去找到了一個比較完美的解決方案應對CPU密集型任務,但是可能有同學會說,我可以使用cluster來做相同的事情,下面我們來做一個使用cluster計算這些任務的情況:
var cluster = require('cluster');
var numCPUs = 8;
function fibo (n) {
return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
}
console.time('8 cluster');
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
var i = 8;
cluster.on('exit', function(worker, code, signal) {
if(!--i){
console.timeEnd('8 cluster');
process.exit(0);
}
});
} else {
console.log(fibo (40));
process.exit(0);
}
代碼上的復雜程度比使用TAGG要高的多,而且如果是動態計算斐波那契數組的結果,編碼將更加困難,需要在fork時掛上不同的參數,出錯的幾率也更大。同時還有更重要的一個事情,如果是創建一個http伺服器,如果4個cluster都在計算fibo,那第5個請求node將無法處理,而是用TAGG則還是能夠正常處理的,所以cluster並不能解決單線程模型的cpu密集計算帶來的阻塞問題,我們看下測試結果:
165580141
165580141
165580141
165580141
165580141
165580141
165580141
165580141
8 cluster: 11925ms
TAGG模塊還有其他更多的功能,比如事件觸發,平滑退出,查看線程工作狀態等等,總之TAGG模塊給node注入了新的活力,讓node一直飽受詬病的處理cpu密集任務問題得到了一個妥善的解決,就算你不擅長c++代碼,也能夠輕松編寫出多線程的真正的非阻塞node程序了。
Ⅲ javascript中怎麼讓主線程暫停一會兒再執行,就像Java中的Thread.sleep()一樣
你可以用setTimeout函數和setInterval函數來達到間隔一段時間後執行某段代碼的需求,但是這兩個函數都是不阻塞線程的。