瀏覽器的單線程模型
在瀏覽器中愕难,JS 的代碼執(zhí)行是單線程模式早龟。就是只有一個線程去處理 JS 代碼,一旦當前隊列中有任務在執(zhí)行猫缭,后續(xù)的任務都會等到當前任務執(zhí)行完畢才會執(zhí)行葱弟。
為什么是單線程?因為瀏覽器在很早就發(fā)明出來了猜丹,當時考慮的主要就是簡單芝加。單線程相較于多線程模式會簡單很多,所以 JS 的執(zhí)行就一直都是一種單線程模式射窒。
這種模式的好處就是實現(xiàn)起來比較簡單藏杖,不用考慮多線程的執(zhí)行順序之類的問題;缺點就是只要有一個任務耗時很長脉顿,后面的任務都會排隊等待蝌麸,會拖延整個程序的執(zhí)行。
在早期的 IE 瀏覽器中弊予,如果有一個 ajax 請求在等待數(shù)據(jù)返回的時候祥楣,頁面經(jīng)常性的卡死無法操作,甚至都不能滾動頁面汉柒。
異步
在正常情況下误褪,所有的任務都是放在棧中按順序執(zhí)行。但是如果碰到耗時比較久的任務呢碾褂?兽间?
實際上,在瀏覽器中正塌,還有一個任務隊列(callback queue)嘀略。這里存放的都是異步執(zhí)行的任務恤溶。
例如此時我們給某一元素綁定了一個 onClick 事件。綁定的事件我們可以放到主線程中去進行綁定帜羊,但是事件的觸發(fā)我們放到了任務隊列中咒程。只有在主線程中的任務都執(zhí)行完畢后才會執(zhí)行任務隊里列里的任務。
setTimeout 和 setInterval 的操作方式時將綁定的事件移除出本次的事件循環(huán)(event loop)讼育,等到下一次事件循環(huán)的時候去檢查是否到了指定時間帐姻。如果到了,就執(zhí)行對應的代碼奶段;如果沒到饥瓷,就等到下一輪的事件循環(huán)(event loop)時再去判斷。
函數(shù)節(jié)流
為了性能的考慮痹籍,有時候有一些頻發(fā)觸發(fā)的事件我們需要做一些相對應的節(jié)流方式呢铆。例如頁面滾動的時候,每滾一次就會觸發(fā)一次蹲缠,感覺有些浪費性能棺克。
我們可以利用 setTimeout 和 clearTimeout 來進行函數(shù)節(jié)流。
var timer;
function always(){
if(timer) clearTimeout(timer);
timer = setTimeout(function() {
console.log('Timer...');
}, 2000);
}
當函數(shù)調(diào)用時吼砂,會進行一個判斷:如果當前的值為 false 逆航,那么就給它設置一個 setTimeout 鼎文;如果在 setTimeout 的時限之內(nèi)再次調(diào)用的話渔肩,就會清除當前的 setTimeout ,并且在重新設置一個 setTimeout 拇惋。只有在時限內(nèi)無動作的話周偎,函數(shù)才會直接結果。
這樣就實現(xiàn)了函數(shù)的節(jié)流撑帖,減少了后續(xù)代碼的等待蓉坎。如果 setTimeout 里面是一個 ajax 請求的話,這個效果會更明顯的胡嘿。節(jié)省了請求的時間蛉艾,也提高了頁面的性能。