人們對 JavaScript的定時器存在廣泛的誤解,認爲它們是線程,其實 JavaScript 是運行於單線程的環境中的,而定時器僅僅只是計劃代碼在將來的某個時間執行。執行時機是不能保證的,由於在頁面的生命週期中,不一樣時間可能有其餘代碼在控制JavaScript進程。在頁面下載完後的代碼運行、事件處理程序、Ajax回調函數都必須使用一樣的線程來執行。實際上,瀏覽器負責進行排序,指派某段代碼在某個時間點運行 的優先級。java
定時器對隊列的工做方式是,當特定時間過去後將代碼插入。注意,給隊列添加代碼並不意味着對它馬上執行,而只能表示它會盡快執行。設定一個 150ms 後執行的定時器不表明到了 150ms 代碼就馬上 執行,它表示代碼會在150ms後被加入到隊列中。若是在這個時間點上,隊列中沒有其餘東西,那麼這段代碼就會被執行,表面上看上去好像代碼就在精確指定的時間點上執行了。其餘狀況下,代碼可能明顯地等待更長時間才執行。web
(1) 某些間隔會被跳過;瀏覽器
(2) 多個定時器的代碼執行之間的間隔可能會比預期的小。bash
爲了不 setInterval()的重複定時器的這 2 個缺點,你能夠用以下模式使用鏈式 setTimeout() 調用。函數
setTimeout(function(){ //處理中
setTimeout(arguments.callee, interval);//次優選項 可寫個function代替
}, interval);
複製代碼
這個模式鏈式調用了setTimeout(),每次函數執行的時候都會建立一個新的定時器。第二個 setTimeout()調用使用了 arguments.callee來獲取對當前執行的函數的引用,併爲其設置另一 個定時器。這樣作的好處是,在前一個定時器代碼執行完以前,不會向隊列插入新的定時器代碼,確保不會有任何缺失的間隔。並且,它能夠保證在下一次定時器代碼執行以前,至少要等待指定的間隔,避 免了連續的運行。這個模式主要用於重複定時器,以下例所示。優化
setTimeout(function(){
var div = document.getElementById("myDiv");
left = parseInt(div.style.left) + 5;
div.style.left = left + "px";
if (left < 200){
setTimeout(arguments.callee, 50);
}
}, 50);
複製代碼
(function(){
function draw(timestamp){
//timestamp 它是一個時間碼(從1970 年1月1日起至今的毫秒數),表示下一次重繪的實際發生時間。
//計算兩次重繪的時間間隔
var drawStart = (timestamp || Date.now()),
diff = drawStart - startTime; //使用 diff 肯定下一步的繪製時間
//把 startTime 重寫爲這一次的繪製時間 startTime = drawStart;
//重繪 UI
requestAnimationFrame(draw);
}
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame,
startTime = window.mozAnimationStartTime || Date.now();
})();
複製代碼
1.setInterval 與 setTimeout 模擬的setInterval有什麼區別?動畫
答:ui
setInterval(() => {
//定時器代碼
}, 200);
複製代碼
每200ms向進程插入一次定時器代碼,在第5ms,205ms,405ms處都插入了定時器代碼,因爲這樣的一條規則:spa
當使用 setInterval()時,僅 當沒有該定時器的任何其餘代碼實例時,纔將定時器代碼添加到隊列中。線程
因此第5ms處,205ms都成功將定時器代碼插入js進程中,可是第405ms處因爲正在執行定時器代碼故插入失敗。使這個 間隔被跳過 ,同時執行完第一次定時器代碼後緊接着執行了第二次定時器代碼,使 多個定時器的代碼執行之間的間隔可能會比預期的小。
setTimeout(function(){
//定時器代碼
setTimeout(arguments.callee, interval);
}, interval);
複製代碼
這個模式鏈式調用了setTimeout(),每次函數執行的時候都會建立一個新的定時器。第二個setTimeout()調用使用了arguments.callee 來獲取對當前執行的函數的引用,併爲其設置另一個定時器。這樣作的好處是,在前一個定時器代碼執行完以前,不會向隊列插入新的定時器代碼,確保不會有任何缺失的間隔。並且,它能夠保證在下一次定時器代碼執行以前,至少要等待指定的間隔,避 免了連續的運行。
2.以下代碼的 clearTimeout(s)是否能夠清楚定時器。
var i = 0
var s = setTimeout(function(){
add()
},300)
function add(){
i++
console.log(123);
console.log(i);
if(i>20){
clearTimeout(s)
}else{
setTimeout(function(){
add()
},300)
}
}
複製代碼
答:可
3.arguments.callee 是什麼意思?
警告:在嚴格模式下,第5版 ECMAScript (ES5) 禁止使用 arguments.callee()。當一個函數必須調用自身的時候, 避免使用 arguments.callee(), 經過要麼給函數表達式一個名字,要麼使用一個函數聲明.