Javascript學習---排程:setTimeout&&setInterval
setTimeout&&setInterval
有時候我們不想要馬上執行函式,而是在未來的特定時間裡執行,這就涉及到排程問題,Javascript提供了setTimeout和setInterval這兩個函式:
(1)setTimeout:允許在指定的時間間隔之後執行函式(一次);
(2)setInterval:允許在執行期間定期地執行函式(多次);
setTimeout
setTimeout的語法如下:
let timerId = setTimeout(func|code, delay[, arg1, arg2...])
其中func|code需要執行的函式或字串程式碼;delay是延遲執行的時間,以毫秒為單位;arg1,arg2...指定是被排程函式的引數。
延遲一秒後執行的例子:
function sayHi() {
alert('Hello');
}
setTimeout(sayHi, 1000);
設定執行函式引數:
function sayHi(phrase, who) {
alert( phrase + ', ' + who );
}
setTimeout(sayHi, 1000, "Hello", "John"); // Hello, John
如果setTimeout的第一個引數是字串程式碼,則Javascript預設根據程式碼生成一個函式,例如:
setTimeout("alert('Hello')", 1000);
但是使用字串程式碼的形式是不推薦的,我們可以使用箭頭函式來代替它,例如:
setTimeout(() => alert('Hello'), 1000);
需要注意的是,有時候我們可能會在執行函式後面加多個括號“()”,這樣setTimeout函式會報錯,例如:
// wrong!
setTimeout(sayHi(), 1000); //undefined
這是因為setTimeout接受的是一個函式的引用,引用的值會傳遞給setTimeout。但如果是一個函式的話,由於sayHi()不返回任何值,故沒有傳遞值給setTimeout,所以結果是undefined
取消排程clearTimeout
我們使用setTimeout來設定排程後,也可以通過clearTimeout來取消排程,例如:
let timerId = setTimeout(...);
clearTimeout(timerId);
由於每次使用setTimeout後都會預設返回一個識別符號,所以取消排程的會則要通過這個識別符號
再看下面的例子:
let timerId = setTimeout(() => alert("never happens"), 1000);
alert(timerId); // timer identifier
clearTimeout(timerId);
alert(timerId); // same identifier (doesn't become null after canceling)
從上面的結果中可以看出,在瀏覽器中timerId是一個數字,而在其他環境中可能會是其他型別資料,例如在Node.js會返回一個附帶其他方法的timer物件
setInterval
setInterval和setTimeout有一樣的語法:
let timerId = setInterval(func|code, delay[, arg1, arg2...])
但是有一點不同的是setInterval會在時間間隔後定期執行,不是隻有一次
為了取消setInterval,我們可以使用clearInterval,例如:
// repeat with the interval of 2 seconds
let timerId = setInterval(() => alert('tick'), 2000);
// after 5 seconds stop
setTimeout(() => { clearInterval(timerId); alert('stop'); }, 5000);
遞迴的setTimeout
前面我們已經知道了,setTimeout僅僅執行一次,為了達到setInterval的效果,我們可以遞迴地使用setTimeout,例如:
/** instead of:
let timerId = setInterval(() => alert('tick'), 2000);
*/
let timerId = setTimeout(function tick() {
alert('tick');
timerId = setTimeout(tick, 2000); // (*)
}, 2000);
相比於setInterval,遞迴使用setTimeout可能更加靈活,因為我們還可以在遞迴中修改delay時間
遞迴使用setTimeout嚴格保證了執行函式被排程的時間間隔,而setInterval卻不能嚴格保證,看下面兩個例子:
let i = 1;
setInterval(function() {
func(i);
}, 100);
let i = 1;
setTimeout(function run() {
func(i);
setTimeout(run, 100);
}, 100);
第一個例子中,func()會在每隔100毫秒後被呼叫,無論func()有沒有執行完,若func()執行時間大於間隔時間,那麼就會出現疊加呼叫的情況,即第一次呼叫的func()還沒執行完,func()的第二次呼叫就開始了;第二個例子中,func()每次執行完後的100毫秒後再次被呼叫。因此,我們應該根據具體情況使用setInterval和遞迴的setInterval
setTimeout(…,0)情況
這裡有一個特殊的情況,當使用setTimeout(...,0)時,它會在當前的其他程式碼執行完後再執行,也就是非同步,例如:
setTimeout(() => alert("World"), 0);
alert("Hello");
這裡它會先輸出"Hello",再輸出"World"