1.介紹
JavaScript 提供定時執行代碼的功能,叫做定時器(timer),主要由setTimeout() 和setInterval() 這兩個函數來完成。
它們向任務隊列添加定時任務
2.setTimeout
setTimeout 函數用來指定某個函數或某段代碼,在多少毫秒之後執行。它返回一個整數,表示定時器的編號,以後可以用來取消這個定時器。
var timerId = setTimeout(func|code, delay);
上面代碼中,setTimeout 函數接受兩個參數,第一個參數func|code 是將要推遲執行的函數名或者一段代碼,第二個參數delay 是推遲執行的毫秒數。
console.log(1);
setTimeout('console.log(2)',1000); //這裏不論設置多久, 哪怕是0 , 也是後執行 console.lo(2)
console.log(3);
// 1
// 3
// 2
上面代碼會先輸出1和3,然後等待1000毫秒再輸出2。
注意,console.log(2) 必須以字符串的形式,作爲setTimeout 的參數。//目前Chrome已經不支持這種方式, 因爲認爲不安全
版本 81.0.4044.138(正式版本) (64 位)
如果推遲執行的是函數,就直接將函數名,作爲setTimeout 的參數。//推薦寫法
function f() {
console.log(2);
}
setTimeout(f, 1000);
setTimeout 的第二個參數如果省略,則默認爲0。
setTimeout(f)
// 等同於
setTimeout(f, 0)
除了前兩個參數,setTimeout 還允許更多的參數。它們將依次傳入推遲執行的函數(回調函數)。
setTimeout(function (a,b) {
console.log(a + b);
}, 1000, 1, 1);
輸出結果: 3
上面代碼中,setTimeout 共有4個參數。最後那兩個參數,將在1000毫秒之後回調函數執行時,作爲回調函數的參數。
還有一個需要注意的地方,
如果回調函數是對象的方法,那麼setTimeout 使得方法內部的this 關鍵字指向全局環境,而不是定義時所在的那個對象。
var x = 1;
var obj = {
x: 2,
y: function () {
console.log(this.x);
}
};
setTimeout(obj.y, 1000) // 1
上面代碼輸出的是1,而不是2。
因爲當obj.y 在1000毫秒後運行時,this 所指向的已經不是obj 了,而是全局環境。
爲了防止出現這個問題,一種解決方法是將obj.y 放入一個函數。
var x = 1;
var obj = {
x: 2,
y: function () {
console.log(this.x);
}
};
setTimeout(function () {
obj.y();
}, 1000);
// 2
上面代碼中,obj.y 放在一個匿名函數之中,這使得obj.y 在obj 的作用域執行,而不是在全局作用域內執行,所以能夠顯示正確的值。
另一種解決方法是,使用bind 方法,將obj.y 這個方法綁定在obj 上面。
var x = 1;
var obj = {
x: 2,
y: function () {
console.log(this.x);
}
};
setTimeout(obj.y.bind(obj), 1000)
// 2
3.setInterval
setInterval 函數的用法與setTimeout 完全一致,區別僅僅在於setInterval 指定某個任務每隔一段時間就執行一次,也就是無限次的定時執行。
var i = 1
var timer = setInterval(function() {
console.log(2);
}, 1000)
上面代碼中,每隔1000毫秒就輸出一個2,會無限運行下去,直到關閉當前窗口。
與setTimeout 一樣,除了前兩個參數,setInterval 方法還可以接受更多的參數,它們會傳入回調函數。
下面是一個通過setInterval 方法實現網頁動畫的例子。
var div = document.getElementById('someDiv');
var opacity = 1;
var fader = setInterval(function() {
opacity -= 0.1;
if (opacity >= 0) {
div.style.opacity = opacity;
} else {
clearInterval(fader);
}
}, 100);
上面代碼每隔100毫秒,設置一次div 元素的透明度,直至其完全透明爲止。
setInterval 的一個常見用途是實現輪詢。下面是一個輪詢 URL 的 Hash 值是否發生變化的例子。
var hash = window.location.hash;
var hashWatcher = setInterval(function() {
if (window.location.hash != hash) {
updatePage();
}
}, 1000);
Note:
setInterval 指定的是“開始執行”之間的間隔,並不考慮每次任務執行本身所消耗的時間。
因此實際上,兩次執行之間的間隔會小於指定的時間。
比如,setInterval 指定每 100ms 執行一次,每次執行需要 5ms,那麼第一次執行結束後95毫秒,第二次執行就會開始。
如果某次執行耗時特別長,比如需要105毫秒,那麼它結束後,下一次執行就會立即開始。
爲了確保兩次執行之間有固定的間隔,可以不用setInterval ,而是每次執行結束後,使用setTimeout 指定下一次執行的具體時間。
var i = 1;
var timer = setTimeout(function f() {
// ...
timer = setTimeout(f, 2000);
}, 2000);
上面代碼可以確保,下一次執行總是在本次執行結束之後的2000毫秒開始。
Note: 上面的code即替換setInterval
4.clearTimeout(),clearInterval()
setTimeout 和setInterval 函數,都返回一個整數值,表示計數器編號。
var id1 = setTimeout(f, 1000);
var id2 = setInterval(f, 1000);
clearTimeout(id1);
clearInterval(id2);
上面代碼中,回調函數f 不會再執行了,因爲兩個定時器都被取消了。
setTimeout 和setInterval 返回的整數值是連續的,也就是說,第二個setTimeout 方法返回的整數值,將比第一個的整數值大1。
Note: setTimeout 和setInterval 兩者之間返回的ID值是在相互基礎上交叉+1的
截圖:
利用這一點,可以寫一個函數,取消當前所有的setTimeout 定時器。//TBD
(function() {
// 每輪事件循環檢查一次
var gid = setInterval(clearAllTimeouts, 0);
function clearAllTimeouts() {
var id = setTimeout(function() {}, 0);
while (id > 0) {
if (id !== gid) {
clearTimeout(id);
}
id--;
}
}
})();
上面代碼中,先調用setTimeout ,得到一個計算器編號,然後把編號比它小的計數器全部取消。
5.實例:debounce 函數 //防抖動
https://wangdoc.com/javascript/async/timer.html#實例:debounce-函數
code:
<!DOCTYPE html>
<html>
<head>
<title>防抖動</title>
<style type="text/css">
textarea{
width: 200px;
height: 200px;
}
</style>
</head>
<body>
<div id="someDiv">hello world</div>
<textarea></textarea>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
(function(){
//console.log(window);
//var dom = $('#someDiv');
//console.log(dom);
//return;
$('textarea').on('keydown', debounce(ajaxAction, 2500)); //防抖動間隔爲2.5s 可以將數值改爲0 效果更佳明顯
function debounce(fn, delay){
var timer = null; // 聲明計時器
return function() {
var context = this;
var args = arguments;
// console.log(args)
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
}
function ajaxAction(para){
console.log(para, 'ajaxAction');
}
})()
</script>
</body>
</html>
6.運行機制
https://wangdoc.com/javascript/async/timer.html#運行機制
7.setTimeout(f, 0) //應用場景有一些, 建議練習一下
https://wangdoc.com/javascript/async/timer.html#settimeoutf-0
code:
<!DOCTYPE html>
<html>
<head>
<title>可以調整事件的發生順序/用戶自定義的回調函數,通常在瀏覽器的默認動作之前觸發</title>
<style type="text/css">
textarea{
width: 200px;
height: 200px;
}
</style>
</head>
<body>
<div id="someDiv">hello world</div>
<input type="button" id="myButton" value="click">
<input type="text" id="input-box">
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
(function(){
//1.可以調整事件的發生順序[這裏目標是先執行父級元素的事件,然後執行子元素的事件]
// var input = document.getElementById('myButton');
// input.onclick = function A() {
// setTimeout(function B() {
// input.value +=' input';
// }, 0)
// };
// document.body.onclick = function C() {
// input.value += ' body' //先執行父級元素的事件
// };
//2.用戶自定義的回調函數,通常在瀏覽器的默認動作之前觸發 -- 想在用戶每次輸入文本後,立即將字符轉爲大寫
document.getElementById('input-box').onkeypress = function (event) {
//this.value = this.value.toUpperCase(); //這種寫法, 只能讓前面輸入的字母大寫, 最後一個可能還是輸入時的樣子
//改寫如下:
var self = this;
setTimeout(function() {
self.value = self.value.toUpperCase();
}, 0);
}
//3.那些計算量大、耗時長的任務,常常會被放到幾個小部分,分別放到setTimeout(f, 0)裏面執行
var div = document.getElementsByTagName('div')[0];
// console.log(div);
// 寫法一 //會看到阻塞 瀏覽器刷新圖標一直在打轉
// for (var i = 0xFFFFFF; i > 0xA00000; i--) {
// div.style.backgroundColor = '#' + i.toString(16); //可以轉換爲 rgb(160, 0, 1)形式
// }
// 寫法二 //會看到沒有阻塞 瀏覽器刷新圖標沒有打轉, 而且可以看到頁面上顏色的漸變效果
var timer;
var i=0x100000;
function func() {
timer = setTimeout(func, 0);
div.style.backgroundColor = '#' + i.toString(16);
if (i++ == 0xFFFFFF) clearTimeout(timer);
}
timer = setTimeout(func, 0);
//4.另一個使用這種技巧的例子是代碼高亮的處理。如果代碼塊很大,一次性處理,可能會對性能造成很大的壓力,那麼將其分成一個個小塊,一次處理一塊,比如寫成setTimeout(highlightNext, 50)的樣子,性能壓力就會減輕。
})()
</script>
</body>
</html>
後續補充
...
|