摘要:這么執行導致的結果是每次的時間必然會大于主線程代碼執行消耗的時間,而當這個主線程代碼執行消耗的時間累加起來超過時,就會出現跳一秒的情況。
拜年
新年伊始,本搬磚汪先給各位老爺們拜個晚年,祝各位技術大牛們在新的一年代碼功底更進一步,家庭幸福美滿!
需求下面進入正題:在翻閱segmentfault社區時看到某巨廠面試要求實現一個倒計時功能,之前也沒有仔細實現過,趁年初來任務還沒來得及分配,趕緊著手實現了一個。
第一版var period = 60*1000*60*2 var end = new Date().getTime() + period var date = new Date(end) var interval = 1000 var count = 0 var startTime = new Date().getTime() console.log("開始時間:" + startTime) function loopInner() { count++ var diff = end - new Date().getTime() var h = Math.floor(diff / (60*1000*60)) var hdiff = diff % (60*1000*60) var m = Math.floor(hdiff / (60*1000)) var mdiff = hdiff % (60*1000) var s = mdiff / 1000 var sCeil = Math.ceil(s) var j = 0 while (j<100000000) { // 放大主線程代碼執行時間 j++ } console.log(h + "小時", m + "分鐘:", s + "秒(精確到毫秒)", sCeil + "秒(進一法)") } function loop() { loopInner() // 首先var j = 0 if (count === 100) { var endTime = new Date().getTime() console.log("結束時間:" + endTime) // 打印開始時間 console.log("時間差毫秒數:" + Number(endTime - startTime) + "對應秒數:" + Number(endTime - startTime) / 1000) console.log("計時器計算秒數:100") } else { return setTimeout(loop, interval) } } loop()
結果如下:
第一版實現我使用的是遞歸的setTimeout方法,原因是之前曾經看到過遞歸的setTimeout能避免setInterval忽視代碼執行時間,而一個事件隊列里只會有一個setInterval事件導致的部分setInterval事件被忽略的情況。這么執行導致的結果是每次setTimeout的時間必然會大于1000ms(1000 + 主線程代碼執行消耗的時間),而當這個主線程代碼執行消耗的時間累加起來超過1s時,就會出現跳一秒的情況。這一版實現方案的結果不盡如人意。
第二版var period = 60 * 1000 * 60 * 2 var end var date = new Date(end) var interval = 1000 var count = 0 var startTime = new Date().getTime() console.log("開始時間:" + startTime) var loop = function () { count++ if (count === 100) { var endTime = new Date().getTime() console.log("結束時間:" + endTime) // 打印開始時間 console.log("時間差毫秒數:" + Number(endTime - startTime) + "對應秒數:" + Number(endTime - startTime) / 1000) console.log("計時器計算秒數:100") return clearInterval(Itvid) } if (!end) { end = new Date().getTime() + period } var diff = end - new Date().getTime() var h = Math.floor(diff / (60 * 1000 * 60)) var hdiff = diff % (60 * 1000 * 60) var m = Math.floor(hdiff / (60 * 1000)) var mdiff = hdiff % (60 * 1000) var s = mdiff / (1000) var roundS = Math.round(s) var j = 0 while (j<100000000) { // 放大主線程代碼執行時間 j++ } console.log(h + "小時:", m + "分鐘:", s + "秒(精確到毫秒)", roundS + "秒(四舍五入)") } var Itvid = setInterval(loop, interval)
結果如下:
這一版的結果比較接近正確答案,利用setInterval不等待執行代碼完成就直接加入隊列的特性(參考setInterval與setTimeout的精確度問題),再加上用Math.round方法修正js的異步方法所造成的幾毫秒的誤差即可。而setInterval畢竟也是瀏覽器的api,同樣是有幾毫秒的差異的。
第三版這一版是我選擇在第一種寫法的基礎上做改良:每次循環中基于此次代碼執行所消耗的時間對下次循環所消耗的時間間隔做修正。
var period = 60 * 1000 * 60 * 2 var startTime = new Date().getTime(); var count = 0 var end = new Date().getTime() + period var interval = 1000 var currentInterval = interval console.log("開始時間:" + startTime) // 打印開始時間 function loop() { count++ var offset = new Date().getTime() - (startTime + count * interval); // 代碼執行所消耗的時間 var diff = end - new Date().getTime() var h = Math.floor(diff / (60 * 1000 * 60)) var hdiff = diff % (60 * 1000 * 60) var m = Math.floor(hdiff / (60 * 1000)) var mdiff = hdiff % (60 * 1000) var s = mdiff / (1000) var sCeil = Math.ceil(s) var sFloor = Math.floor(s) currentInterval = interval - offset // 得到下一次循環所消耗的時間 var j = 0 while (j<100000000) { // 放大主線程代碼執行時間 j++ } console.log("時:"+h, "分:"+m, "毫秒:"+s, "秒向上取整:"+sCeil, "代碼執行時間:"+offset+"ms", "下次循環間隔"+currentInterval+"ms") // 打印 時 分 秒 代碼執行時間 下次循環間隔 if (count === 100) { var endTime = new Date().getTime() console.log("結束時間:" + endTime) // 打印開始時間 console.log("時間差毫秒數:" + Number(endTime - startTime) + "對應秒數:" + Number(endTime - startTime) / 1000) console.log("計時器計算秒數:100") } else { setTimeout(loop, currentInterval) } } setTimeout(loop, currentInterval)
結果如下:
對于同步代碼執行耗時不是過大(幾十毫秒到幾百毫秒之間)的情況,通過實驗得到結果:
setInterval > 修正時間間隔的遞歸setTimeout > 遞歸setTimeout
疑問業務場景中是否存在同步代碼執行時間超過數秒的情況?
業務場景中實現倒計時的標準做法?
從服務端端獲取開始時間會有時間損耗(http傳輸的耗時),這個耗時有沒有方法規避?
依然遺留這些問題存在,還請各位不吝賜教。
參考資料JS實現活動精確倒計時
w3.org
javascript線程解釋(setTimeout,setInterval你不知道的事)
歡迎訪問我的博客
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93219.html
摘要:瀏覽器渲染進程瀏覽器內核進程,內部是多線程的默認每個頁面一個進程,互不影響。事件觸發線程歸屬于瀏覽器而不是引擎,用來控制事件循環可以理解成引擎自己都忙不過來,需要瀏覽器另開線程協助。 線程和進程 進程和線程的概念可以這樣理解: 進程是一個工廠,工廠有它的獨立資源--工廠之間相互獨立--線程是工廠中的工人,多個工人協作完成任務--工廠內有一個或多個工人--工人之間共享空間 工廠有多個工人...
摘要:而進程是多線程的,它主要包含以下主要線程渲染線程負責渲染瀏覽器界面,解析,,構建樹和樹,布局和繪制等。且加載解析執行會阻止解析器往下執行,要強調渲染和下載是不沖突的,渲染是線程在執行,下載是下載線程在執行,瀏覽器多線程。 了解瀏覽器線程基礎 一個頁面的呈現主要是由瀏覽器渲染進程實現的(render進程),主要作用為頁面的渲染,腳本執行,事件處理等。而render進程是多線程的,它主要包...
閱讀 2045·2023-04-26 02:23
閱讀 1789·2021-09-03 10:30
閱讀 1351·2019-08-30 15:43
閱讀 1191·2019-08-29 16:29
閱讀 531·2019-08-29 12:28
閱讀 2332·2019-08-26 12:13
閱讀 2169·2019-08-26 12:01
閱讀 2400·2019-08-26 11:56