JavaScript?定時(shí)器關(guān)鍵點(diǎn)及使用場(chǎng)景解析
正文
JavaScript 定時(shí)器是實(shí)現(xiàn)循環(huán)行為甚至觸發(fā)延遲操作的好功能。無(wú)論有什么基于時(shí)間的邏輯,定時(shí)器都可以提供支持。在 JavaScript 中有兩個(gè)定時(shí)器函數(shù):setTimeout
和 setInterval
。接下來(lái)看看有哪些定時(shí)器以及它們是如何工作的。
關(guān)于計(jì)時(shí)器的關(guān)鍵點(diǎn)
在深入了解定時(shí)器的具體細(xì)節(jié)之前,有幾個(gè)非常關(guān)鍵的點(diǎn)需要記住。
并不完全準(zhǔn)確
定時(shí)器要么在一定秒數(shù)后觸發(fā)一個(gè)動(dòng)作,要么在每次指定的超時(shí)結(jié)束時(shí)重復(fù)一個(gè)動(dòng)作。但是,盡管期望可能是它們精確到秒,但事實(shí)并非如此。
這些計(jì)時(shí)器的規(guī)范聲明它們將使用時(shí)間參數(shù)(如指定的秒數(shù))作為最短等待時(shí)間。但如果還有其他任務(wù)需要先完成,它們肯定會(huì)花費(fèi)更長(zhǎng)的時(shí)間。
只有當(dāng)你邏輯依賴于精確的時(shí)間測(cè)量時(shí),這才會(huì)成為問(wèn)題,比如讓時(shí)鐘通過(guò) setInterval
回調(diào)來(lái)計(jì)算秒數(shù)。
異步函數(shù)
這意味著它們?cè)谕瓿芍安粫?huì)停止程序流。即使指定 0
作為超時(shí)值,它們的行為仍然是異步的。
這意味著這些函數(shù)會(huì)將對(duì)想要觸發(fā)的函數(shù)的引用添加到事件循環(huán)中,所以即使在超時(shí)值上指定了 0
,引用也會(huì)在接下來(lái)發(fā)生的所有事情之后排隊(duì)。
setTimeout
setTimeout
函數(shù)可能是最容易理解的函數(shù),因?yàn)橹饕繕?biāo)是在幾秒后觸發(fā)一個(gè)函數(shù),只會(huì)執(zhí)行一次。
這個(gè)函數(shù)參數(shù)如下:
- 要執(zhí)行的函數(shù)引用:這是時(shí)間一到就會(huì)觸發(fā)的代碼邏輯。
- 函數(shù)執(zhí)行前的等待的秒數(shù)
- 然后所有其他參數(shù)以相同的順序傳遞給執(zhí)行的函數(shù)。
下面的代碼將在 3
秒后打印 Hello World
:
setTimeout(console.log, 3000, "Hello", "World");
等效于以下代碼:
setTimeout( (strHello, strWorld) => { console.log(strHello, strWorld); }, 3000, "Hello", "World" );
setTimeout
是一種特殊類型的異步函數(shù),因此無(wú)論在其后編寫(xiě)什么代碼,都將在觸發(fā)該函數(shù)之前執(zhí)行,如下:
console.log("執(zhí)行了第 1 行代碼"); setTimeout(() => { console.log("執(zhí)行了第 3 行代碼"); }, 1000); console.log("執(zhí)行了第 5 行代碼"); console.log("執(zhí)行了第 6 行代碼"); setTimeout(function () { console.log("執(zhí)行了第 8 行代碼"); }, 0); console.log("執(zhí)行了第 10 行代碼");
輸出的結(jié)果如下:
執(zhí)行了第 1 行代碼
執(zhí)行了第 5 行代碼
執(zhí)行了第 6 行代碼
執(zhí)行了第 10 行代碼
執(zhí)行了第 8 行代碼
執(zhí)行了第 3 行代碼
請(qǐng)注意第 3 行和第 8 行是如何最后執(zhí)行的,即使第 8 行的超時(shí)為 0
。
在討論 setTimeout
之前,如果設(shè)置超時(shí)值然后意識(shí)到必須停止它會(huì)發(fā)生什么?可以定義一個(gè)變量保存 setTimeout
的返回值(計(jì)時(shí)器 ID),則可以使用 clearTimeout
函數(shù)在超時(shí)之前停止計(jì)時(shí)器。
console.log("執(zhí)行了第 1 行代碼"); const timerId = setTimeout(() => { console.log("執(zhí)行了第 3 行代碼"); }, 1000); console.log("執(zhí)行了第 5 行代碼"); clearTimeout(timerId); console.log("執(zhí)行了第 6 行代碼"); setTimeout(function () { console.log("執(zhí)行了第 8 行代碼"); }, 0); console.log("執(zhí)行了第 10 行代碼");
執(zhí)行結(jié)果如下,少了一個(gè)定時(shí)器的輸出:
執(zhí)行了第 1 行代碼
執(zhí)行了第 5 行代碼
執(zhí)行了第 6 行代碼
執(zhí)行了第 10 行代碼
執(zhí)行了第 8 行代碼
setInterval
setInterval
函數(shù)與 setTimeout
非常相似,但它不是只觸發(fā)一次函數(shù),而是一直觸發(fā)函數(shù)直到停止。
此函數(shù)的簽名與 setInterval
函數(shù)的簽名完全相同,所有參數(shù)的也相同。
const names = ["劉備", "關(guān)羽", "張飛", "趙云", "黃忠"]; function sayHi(list) { let name = list[Math.round(Math.random() * 10) % 4]; console.log("你好!", name); } console.log("蜀漢五虎將"); const intervalID = setInterval(sayHi, 1000, names); setTimeout(() => { clearTimeout(intervalID); }, 4000);
上面的代碼將啟動(dòng)一個(gè)每 1
秒觸發(fā)一次的循環(huán),當(dāng)它觸發(fā)時(shí),將選擇一個(gè)隨機(jī)名稱并打印字符串 你好! <name>。設(shè)置了 4 秒的超時(shí)時(shí)間,通過(guò)調(diào)用 clearTimeout
函數(shù)來(lái)結(jié)束無(wú)限循環(huán)。當(dāng)然,也可以使用 clearInterval
函數(shù),但由于它們使用相同的計(jì)時(shí)器池,可以互換使用它們。
區(qū)別
setTimeout
僅觸發(fā)一次表達(dá)式,而 setInterval
在給定的時(shí)間間隔后保持定期觸發(fā)表達(dá)式(除非手動(dòng)終止)。
使用場(chǎng)合
除了執(zhí)行定時(shí)操作,結(jié)合 Promise
方法,結(jié)合 setTimeout
可以實(shí)現(xiàn)休眠功能。
const sleep = (ms) => { return new Promise((resolve) => setTimeout(resolve, ms)); }; const asyncFoo = async () => { await sleep(2000); console.log(" 等待2秒輸出"); await sleep(1000); console.log(" 等待1秒輸出"); }; console.log("開(kāi)始執(zhí)行"); asyncFoo();
Promise
結(jié)合 setInterval
可以實(shí)現(xiàn)一些數(shù)據(jù)的最大檢測(cè)次數(shù),如某個(gè)數(shù)據(jù)通過(guò)API驗(yàn)證,驗(yàn)證結(jié)果無(wú)法給出正常結(jié)果,超過(guò)一定次數(shù)提示錯(cuò)誤。
const fakeApiCheck = async () => { console.log("檢查中..."); return Math.random() > 0.8; }; const asyncInterval = async (callback, ms, triesLeft = 5) => { return new Promise((resolve, reject) => { const interval = setInterval(async () => { if (await callback()) { resolve(); clearInterval(interval); } else if (triesLeft <= 1) { reject(); clearInterval(interval); } triesLeft--; }, ms); }); }; const dataCheck = async () => { try { await asyncInterval(fakeApiCheck, 500); } catch (e) { console.log("驗(yàn)證錯(cuò)誤"); } console.log("驗(yàn)證完成!"); }; dataCheck();
總結(jié)
計(jì)時(shí)器是生成重復(fù)或延遲行為的絕佳函數(shù),它們非常有用,尤其是當(dāng)必須在某些基于時(shí)間的條件下與其他服務(wù)進(jìn)行交互時(shí)。超時(shí)和間隔都可以在使用 clearInterval
或者 clearTimeout
函數(shù)觸發(fā)之前強(qiáng)制停止。
以上就是JavaScript 定時(shí)器關(guān)鍵點(diǎn)及使用場(chǎng)景解析的詳細(xì)內(nèi)容,更多關(guān)于JavaScript 定時(shí)器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信小程序 判斷手機(jī)號(hào)的實(shí)現(xiàn)代碼
這篇文章主要介紹了微信小程序 判斷手機(jī)號(hào)的實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-04-04

8個(gè)JS的reduce使用實(shí)例和reduce操作方式

微信小程序 flex實(shí)現(xiàn)導(dǎo)航實(shí)例詳解

微信小程序 數(shù)據(jù)遍歷的實(shí)現(xiàn)

微信小程序返回多級(jí)頁(yè)面的實(shí)現(xiàn)方法

競(jìng)態(tài)條件Race condition及如何避免的三種方案詳解