參考權威:MDN
setTimeout()與setInterval()
由瀏覽器提供的WebAPI,我們將請求丟到瀏覽器來做出非同步請求的感覺
先談談setTimeout()
setTimeout( callback_func, milliseconds, parameter1, parameter2, ...)
x毫秒後執行callback function,callback_func必帶,其他參數則是optional,毫秒沒寫的話預設值為0
後面那些parameters是給callback_func的參數,範例如下,注意global a 和local a的不同
output第一個"Usada"是第四行的結果
var a = "Usada" setTimeout((a)=>{var a = a + " Pekora"; console.log(a)}) //undefined Pekora setTimeout((a)=>{var a = a + " Pekora"; console.log(a)},0,"Usada") //Usada Pekora console.log(a) //Usada setTimeout(()=>console.log(a),2000) //Usada //output Usada undefined Pekora Usada Pekora Usada
setTimeout的請求會被丟到WebAPI端,丟出去時便從stack暫時移除了
這也是為啥第四行"console.log(a)"會先執行的緣故
那麼到底WebAPI的timer怎麼計時呢?
setTimeout(()=>console.log("fin"),10000) for (let x = 1;x<9999999999;x++){ var y = y + Math.sqrt(x) }
上面單獨執行for迴圈的話,我的電腦大概需要40.639s
連同setTimeout一起執行,則需約40.63s
這說明了
setTimeout被丟入WebAPI時便開始計時
在WebAPI timer數完後,會將callback function丟入task queue
直到stack內該執行的都執行完畢後(when the stack is empty),task queue才依先後順序丟入stack執行
因此才會說setTimeout的計時不是真的時間
以上面例子來看,當計算量很大且你又把setTimeout寫在計算式前的時候
結果上來說看不到setTimeout想要停留的那10秒
想撐十秒,得把setTimeout移到for迴圈之後~
setTimeout本身是什麼?
也許這樣描述有點怪...請看常見的code
function sayHi(who) { alert(`Hello ${who}!`); } let hiPeko = setTimeout(sayHi, 2000, 'Pekora');
通常會指定setTimeout給一個var或let,如果你console.log該變數,會得到一個整數值
表示'timerID'
存成變數的用意在於要清除timer時才找的到人
clearTimeout( timer_ID )
談談setInterval()
setIInterval( callback_func, milliseconds, parameter1, parameter2, ...)
基本觀念同setTimeout(),差別在於setInterval()是每隔固定毫秒就會執行一次callback function
一樣常常用var/let做成變數方便後續清除找人,要清除的話使用clearInterval(timer_ID)
就這樣~講完啦!
進階使用細節
setInterval()所設定的時間間隔,其實包含了其callback_func執行的運算時間
舉例來說:設定時間間隔1000ms,callback_func運算將耗掉400ms,那麼剩下的interval時間僅有600ms
也就是說400ms運算完console.log出結果,到進入下一個interval開始執行運算,只剩600ms
setTimeout()則是保證每次callback_func執行間隔值為固定你給的那個毫秒值
所以寫成遞迴方式實作setInterval效果時,能確保每個cbf執行間隔一致
//每隔1秒印出"hi Pekora!" setTimeout(function hiPeko() { console.log("hi Pekora!"); setTimeout(hiPeko, 1000); }, 1000);
requestAnimationFrame()介紹
用於瀏覽器rerender時,專門run動畫的setInterval()現代版
creativejs一篇詳細說明的文章
簡言之,requestAnimationFrame()用於解決setTimeout和setInterval在渲染動畫時無法有效使用CPU資源的問題
MDN提供的語法範例如下
function draw() { // Drawing code goes here requestAnimationFrame(draw); } draw();
注意到了嗎?
沒錯!沒有time interval rate參數!
因為requestAnimationFrame()的動畫渲染時間間隔取決於使用者的瀏覽器本身的刷新速率(通常為60fps)
且並非固定一直維持這個刷新速率,當瀏覽器有需要時才是!
所以你切到其他分頁時,舊的頁面內的動畫不會一直狂刷新耗資源
使用requestAnimationFrame()的好處還包含能統一你做的網頁的動畫刷新(save your CPU~)
須留意老舊瀏覽器的支援性就是了...
就是需要給定fps怎辦?
實務上需要強制給定requestAnimationFrame()刷新頻率的話
還記得前面說setTimeout保證執行間隔的這件事嗎?
對的~
用setTimeout + requestAnimationFrame的邏輯來實作(以fps = 15舉例)
var fps = 15; function draw() { setTimeout(function() { requestAnimationFrame(draw); // Drawing code goes here }, 1000 / fps); }
固定時間間隔刷新動畫
除了fps外,想寫成固定時間間隔刷新也可以
code example from joelambert
(好code,看不懂的話就再看十次Ovo)
window.requestInterval = function (fn, delay) { //確保瀏覽器支援性 if ( !window.requestAnimationFrame && !window.webkitRequestAnimationFrame && !(window.mozRequestAnimationFrame && window.mozCancelRequestAnimationFrame) && // Firefox 5 ships without cancel support !window.oRequestAnimationFrame && !window.msRequestAnimationFrame ) return window.setInterval(fn, delay); var start = new Date().getTime(), handle = new Object(); function loop() { var current = new Date().getTime(), delta = current - start; if (delta >= delay) { fn.call(); start = new Date().getTime(); } handle.value = requestAnimFrame(loop); } handle.value = requestAnimFrame(loop); return handle; };