使用setInterval與setTimeout需要注意的部分
越是簡單的東西,有時候越是我們時常忽略需要注意的部分,這篇文章將會介紹setInterval與setTimeout一些小觀念卻重要的部分。
基本介紹
setTimeout與setInterval都是始於web api提供的方法,可以透過window來查看。
- setTimeout:只會執行一次
- setInterval:輪詢執行多次
常見的使用範例
如果將setTimeout放到迴圈內會發生什麼事情:
1 | for(let i=0; i<10; i++){ |
你猜到答案了嗎?輸出的i全部都會是10。
如果是這樣呢?
1 | for(let i=0; i<10; i++){ |
因為把setTimeout用function包起來,因此可以確保每次呼叫都是使用不同的i。
關於setInterval
在開發前端的時候,有時候為了做到輪詢的效果,會使用setInterval這個方法。
讓我們先來看看這段程式:
1 |
|
你覺得這段程式在做什麼事情,每隔1秒log一次嗎?
事實上這裡的1000指的是對程式執行而言的1秒。
這很奇怪嗎?事實上很正常,因為這1秒包含了程式執行的時間。
要做到真正的delay效果,可以搭配使用setTimeout。
1 |
|
setTimeout為0的小技巧
可能很多人看過網路上一些關於setTimeout為0的陷阱題:
1 | let test = function() { |
event loop執行流程
function test放進call stack
callstack: [ test() ]輸出a
callstack: [ test() , log a ]執行setTimeout
callstack: [ test() , setTimeout ]
web api: []拿到web api,丟入web api內執行等待時間
callstack: [ test() ]
web api: [ setTimeout 0 sec ]輸出c
callstack: [ test(), log c ]
web api: [ setTimeout 0 sec ]test()執行完畢
callstack: []
web api: [ setTimeout 0 sec ]
event queue: []將執行完的web api丟到event loop
callstack: []
web api: []
event queue: [ callback-setTimeout() ]callback丟回callstack, 執行callback
callstack: [ callback-setTimeout() ]
web api: []
event queue: []執行callback內的log
callstack: [ callback-setTimeout(), log ]
web api: []
event queue: []
小技巧範例
如果今天我有一個程式希望click之後跳到已經render出來的DOM
1 | const onClick = () => { |
這個時候你會發現,寫的順序明明就是先畫畫面再scroll到目標的element ref呀?為什麼沒反應
因為如果你現在執行scroll的話,事實上是未rerender時的VDOM,那要怎麼做呢?
可以搭配使用web api做callback,callback開始執行的時候,就會是rerender過後的樣子了。
1 | const onClick = () => { |
setInterval需要注意的部分
剛剛上面有提到setInterval如果單純直接使用,時間間隔會不如預期。
假設今天在React上有這個程式在useEffect,可以怎麼優化呢?
有興趣的話可以玩玩看這個範例:
1 | const [count, setCount] = useState(0); |
來設定個setTimeout確保1秒後執行callback
1 | const [count, setCount] = useState(0); |
再還要更進階探討其他部分了,如果今天畫面中有按鈕可以改變count,那我的程式可疼會改成:
1 | const [count, setCount] = useState(0); |
當你點擊之後…你發現什麼問題了嗎?哎呀… 竟然console.log出0和1了呀……
而且還一直每隔1秒各跑出來,這就是陷阱啦~
請注意,setInterval不像setTimeout一樣會執行程式完之後進行garbage collection。
因此你要記得將舊的邏輯clear掉,不然可能會發生多工的狀況。
1 | const [count, setCount] = useState(0); |
改成這樣就能正常的每隔1秒後輸出count了。