2023年4月29日 星期六

[Javascript] 潛藏 setTimeout 的陷阱


setTimeoutsetInterval 常用在倒數計時或是提醒等,一些時間有關的實作,然而精準校時卻是不可能的任務,只能盡可能減少誤差。


使用方法

說明與範例
非同步函式(Asynchronous function)

setTimeout不會使得任何執行暫停

console.log(new Date());

setTimeout(() => {
  console.log(new Date());
}, 5000);
setTimeout(() => {
  console.log(new Date());
}, 3000);
setTimeout(() => {
  console.log(new Date());
}, 1000);

可以視同三個Timer同時開始倒數,而非停止等候5秒完成執行後再往下一項執行,因此輸出內容:

Sat Apr 29 2023 21:58:34
Sat Apr 29 2023 21:58:35
Sat Apr 29 2023 21:58:37
Sat Apr 29 2023 21:58:39


零延遲 (Zero deplay)

setTimout 參數第二位置傳入 delay 代表指定時間後執行第一個位置的 function,先看下面這個例子:

console.log("This is first line");

setTimeout(() => {
	console.log("This is second line");
}, 0);

console.log("This is third line");

輸出的結果是

This is first line
This is third line
This is second line

當設定成 0 時,預期是「馬上」執行,但更精準地說其實是下一個 event loop,也就是 setTimeout 會被放到 queue 裡等待下一次執行。

以這個例子來看,delay只能當作最短的執行時間。

const s = new Date().getSeconds();

setTimeout(function() {
	// prints out "2", meaning that the callback is not called immediately after 500 milliseconds.
	console.log("Ran after " + (new Date().getSeconds() - s) + " seconds");
}, 500)

while (true) {
	if (new Date().getSeconds() - s >= 2) {
		console.log("Good, looped for 2 seconds")
		break;
    }
}

可以當作 setTimeout 地位是最低的,因此即使設定的 delay 時間雖然到了,只能等待 queue 其他事件處理完成。

Good, looped for 2 seconds
Ran after 2 seconds