詳解JavaScript如何實現(xiàn)更短時間的延時函數(shù)
在項目開發(fā)中,經(jīng)常能遇到需要延時執(zhí)行的需求,比如實現(xiàn)一個定時器功能。
使用setTimeout實現(xiàn)延時函數(shù)
最常見的方式的就是使用setTimeout
函數(shù)來實現(xiàn)了
// 延時 function sleep(time = 1000) { const promise = new Promise((resolve) => { setTimeout(() => { resolve(true); }, time); }); return promise; } // 使用示例 async function run() { // do something // 延時2秒 await sleep(2000); console.log("sleep end!"); // do something } run();
延時精度測試
// 延時時間設(shè)置為`5ms`,循環(huán)執(zhí)行`1000`次,理論執(zhí)行時間為`5000ms`。 async function run() { console.time("run"); for (let i = 0; i < 1000; i++) { await sleep(5); } console.timeEnd("run"); } run(); // 以下為在瀏覽器console中的5次運行結(jié)果(運行結(jié)果在不同電腦配置下可能有出入) // run: 5776.0888671875 ms // run: 5775.033935546875 ms // run: 5833.064208984375 ms // run: 5804.203857421875 ms // run: 5807.372802734375 ms
如上所示:這樣實現(xiàn)其實已經(jīng)可以滿足大部分需求了,但是總還會有一些特別的情況。
比如我想延時1毫秒,使用上面的方式就無能為力了,因為setTimeout
在瀏覽器的實現(xiàn)中限定了最小間隔時間為4ms
,在設(shè)定小于這個時間時,會默認(rèn)更改為4ms
,因此這個延時函數(shù)理論上適用于4ms
以上的延時情況。
那有沒有其他方式去實現(xiàn)更短時間的延時呢?
使用同步方式實現(xiàn)延時函數(shù)
后來想到不使用異步方式,而采用同步方式來實現(xiàn)延時,使用循環(huán)來阻塞代碼執(zhí)行以達(dá)到延時的效果。
// 延時 function sleep(time = 1) { const start = performance.now(); let delaying = true; while (delaying) { const end = performance.now(); const delay = end - start; if (delay >= time) { delaying = false; } } } // 使用示例 async function run() { // do something // 延時1ms sleep(1); console.log("sleep end!"); // do something } run();
延時精度測試
// 延時時間設(shè)置為`1ms`,循環(huán)執(zhí)行`1000`次,理論執(zhí)行時間為`1000ms` function run() { console.time("run"); for (let i = 0; i < 1000; i++) { sleep(1); } console.timeEnd("run"); } run(); // 以下為在瀏覽器console中的5次運行結(jié)果(運行結(jié)果在不同電腦配置下可能有出入) // run: 1000.179931640625 ms // run: 1000.447998046875 ms // run: 1000.274169921875 ms // run: 1000.135009765625 ms // run: 1000.19091796875 ms // 延時時間設(shè)置為`0.1ms`,循環(huán)執(zhí)行`1000`次,理論執(zhí)行時間為`100ms` function run2() { console.time("run"); for (let i = 0; i < 1000; i++) { sleep(.1); } console.timeEnd("run"); } // 以下為在瀏覽器console中的5次運行結(jié)果(運行結(jié)果在不同電腦配置下可能有出入) // run: 167.093017578125 ms // run: 166.85302734375 ms // run: 167.038818359375 ms // run: 166.60498046875 ms // run: 167.81787109375 ms
可以看出,如果使用同步方法做延時,精確度和最小延時時間都有突破性提高。
但是用此方式有一個極大的弊端,阻塞代碼運行及瀏覽器渲染。因為js
為單線程運行,以同步方式做延時就會導(dǎo)致其他代碼執(zhí)行阻塞,瀏覽器頁面渲染也被阻塞。
因此該方式只適用于極小時間的延時,或者在web worker
中使用。
那有沒有方法即使用異步的方式延時,又能突破setTimeout
的4ms
時間限制呢?
使用MessageChannel實現(xiàn)延時函數(shù)
后來想到是不是能利用MessageChannel
的消息傳輸來實現(xiàn)延時作用。 然后就使用此API
實現(xiàn)了一版延時函數(shù)
// 延時 function sleep(time = 1) { const channel = new MessageChannel(); function timeout(callback) { channel.port1.onmessage = callback; channel.port2.postMessage(null); } return new Promise((resolve) => { const start = performance.now(); const cb = () => { const end = performance.now(); const delay = end - start; if (delay >= time) { resolve(); } else { timeout(cb); } }; timeout(cb); }); } // 使用示例 async function run() { // do something // 延時1ms await sleep(1); console.log("sleep end!"); // do something } run();
延時精度測試
// 延時時間設(shè)置為`1ms`,循環(huán)執(zhí)行`1000`次,理論執(zhí)行時間為`1000ms` async function run() { console.time("run"); for (let i = 0; i < 1000; i++) { await sleep(1); } console.timeEnd("run"); } run(); // 以下為在瀏覽器console中的5次運行結(jié)果(運行結(jié)果在不同電腦配置下可能有出入) // run: 1045.72607421875 ms // run: 1049.069091796875 ms // run: 1064.760986328125 ms // run: 1070.867919921875 ms // run: 1079.14892578125 ms // 延時時間設(shè)置為`0.1ms`,循環(huán)執(zhí)行`1000`次,理論執(zhí)行時間為`100ms` async function run2() { console.time("run"); for (let i = 0; i < 1000; i++) { await sleep(.1); } console.timeEnd("run"); } // 以下為在瀏覽器console中的5次運行結(jié)果(運行結(jié)果在不同電腦配置下可能有出入) // run: 207.505126953125 ms // run: 193.7880859375 ms // run: 199.333984375 ms // run: 207.157958984375 ms // run: 196.8740234375 ms
可以看出使用此方式也能得到很好的效果,而且使用了異步方式來實現(xiàn),缺點可能就增加了cpu
工作,會一定程度上影響性能。
從以上三種實現(xiàn)方式來看,在延時時間短到1ms
以下時,即使有方法實現(xiàn),精確度也會大大降低。并且在實現(xiàn)短時間延時函數(shù)時使用到了performance.now
函數(shù),此函數(shù)的精確度也無法保證,可能進(jìn)一步加劇了延時不準(zhǔn)確的問題。
實際上在瀏覽器中這么短時間的延時需求并不常見,相似的需求在服務(wù)端實現(xiàn)或許能有更好的表現(xiàn)。
這里只是針對在瀏覽器環(huán)境中實現(xiàn)短時間延時的方法做了一定的探討和研究,供大家參考(nodejs不在本次討論范圍)。
到此這篇關(guān)于詳解JavaScript如何實現(xiàn)更短時間的延時函數(shù)的文章就介紹到這了,更多相關(guān)JavaScript延時函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
jquery下onpropertychange事件的綁定方法
用了onchange事件,但是在輸入的時候,用Javascript計算出并填值的那一列并不會響應(yīng)onchange 事件。2010-08-08JavaScript根據(jù)json生成html表格的示例代碼
這篇文章主要介紹了JavaScript根據(jù)json生成html表格的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-10-10用cookies實現(xiàn)的可記憶的樣式切換效果代碼下載
比較不錯的用cookies實現(xiàn)的可記憶的樣式切換效果,這個思路也在一定程序,方便客戶的長期使用。2007-12-12JS實現(xiàn)十字坐標(biāo)跟隨鼠標(biāo)效果
這篇文章給大家分享一下通過JS實現(xiàn)十字坐標(biāo)跟隨鼠標(biāo)效果的代碼,有需要的朋友參考學(xué)習(xí)下吧。2017-12-12