JavaScript中的定時控制
你可能知道,JavaScript遵循事件驅動的編程范例。這意味著一些行為可以激活一些響應,并且這些響應僅在發生特定的行為時才被激活。我們稱這些行為 events (事件),和響應 callbacks (回調)。連續的事件流被稱為 event stream (事件流)。
這些行為發生的速度不是我們能手動控制的。但是我們可以控制何時和如何激活正確的響應。有一些技術為我們提供精確的控制。
- Throttle
- Debounce
- Immediate
Throttle
在現代瀏覽器中,幀速率為60fps是流暢性能的目標,給定我們16.7ms的時間預算用于響應一些事件所有需要的更新。這樣可以推斷,如果每秒發生 n 個事件并且回調執行,需要 t 秒的時間,為了流暢運行,
1 / n >= t
如果 t 以毫秒為單位,
1000 / n >= t
如果你曾經使用mousemove事件,你會知道產生 mousemove 事件的數量每秒可以超過60次。如果我們的回調需要超過16.7ms,那就開始凌亂了。
var then = 0;
function log() {
var now = Date.now();
if (1000 / (now - then) > 60) {
console.log('It\'s over 9000!!!');
}
then = now;
}
window.onmousemove = log;
實現
Throttle 允許我們限制我們激活響應的數量。我們可以限制每秒回調的數量。反過來,也就是說在激活下一個回調之前要等待多少時間;
var delta = 1000;
var then = 0;
function log() {
console.log('foo');
}
function throttledLog() {
var now = Date.now();
if (now - then >= delta) {
log();
then = now;
}
};
window.onmousemove = throttledLog;
我們可以用 fps 替換 delta ,并推斷出不同的代碼。
var fps = 60;
...
function throttledLog() {
var now = Date.now();
if (1000 / (now - then) <= fps) {
log();
then = now;
}
};
window.onmousemove = throttledLog;
我們也可以通過使用 setTimeout 來實現相同的結果。 但是,不是檢查時間差,而是檢查狀態變化。
第一次,我們可以安全地激活回調。一旦完成,只有在等待 delta 時間之后才能再次激活回調。
var delta = 1000;
var safe = true;
function log() {
console.log('foo');
}
function throttledLog() {
if (safe) {
log();
safe = false;
setTimeout(function() {
safe = true;
}, delta);
}
};
window.onmousemove = throttledLog;
Debounce
這個術語-去抖動 來自電子學的領域,手動開關輸入的信號被發送到數字電路中。在電子學中,當你按一個物理按鈕一次,數字電路可能讀到多個按壓,因為按鈕的物理屬性(金屬觸點,彈簧,磨損件等)。
去抖動意味著采集到的所有這些波動的信號,并把它們當作一個。
例子
一個簡單的例子已經存在于JS中: keydown vs keyup 。假設您正在處理一個項目,并且需要輸入內容。但是你想要每次敲擊鍵盤得到一個字符。輸入時,如果長按一個鍵, keydown 事件將連續被觸發,但是 keyup 事件只有在按鍵被釋放時才會觸發。
window.onkeyup = function() {
console.log('onkeyup');
}
window.onkeydown = function() {
console.log('onkeydown');
}
這種行為上的差異對于確定輸入是否已完成是有用的。在示例場景中,它是你將使用的keyup事件。在某種程度上,我們可以說 keydown 是原始輸入, keyup 是去抖動輸入。
實現
當事件發生時,我們不會立即激活回調。相反,我們等待一定的時間并檢查相同的事件是否再次觸發。如果是,我們重置定時器,并再次等待。如果在等待期間沒有發生相同的事件,我們就立即激活回調。
var delta = 1000;
var timeoutID = null;
function log() {
console.log('foo');
}
function debouncedLog() {
clearTimeout(timeoutID); // reset timer
timeoutID = setTimeout(function() {
// wait for some time
// and check if event happens again
log();
}, delta);
};
window.onkeydown = debouncedLog;
Immediate
Immediate是Debounce的精確版本。比起 Debounce 的 等待后續事件觸發,然后再激活回調,Immediate 是 立即激活回調,然后等待后續事件在一定時間內觸發。
實現
就像Throttle的情況一樣,我們需要一個狀態變量來檢查是否應該激活我們的回調。我們在Debounce不需要一個,因為 timeoutID 隱式管理這部分。
var delta = 1000;
var timeoutID = null;
var safe = true;
function log() {
console.log('foo');
}
function immediatedLog() {
if (safe) {
log();
safe = false;
}
clearTimeout(timeoutID);
timeoutID = setTimeout(function() {
safe = true;
}, delta);
};
window.onkeydown = immediatedLog;
結論
在這篇文章中,我們已經探索了用作定時函數的最常見的技術。 雖然這篇文章很長,但它仍然缺乏一個堅實的演示。
來自:http://www.css88.com/archives/6589