setTimeout,它就是一個定時器,用來指定某個函數在多少毫秒之后執行。
setTimeout用法
vartimeoutID=setTimeout(function[,delay,arg1,arg2,...]);vartimeoutID=setTimeout(function[,delay]);vartimeoutID=setTimeout(code[,delay]);
第一個參數為函數或可執行的字符串(比如alert('test'),此法不建議使用)
第二個參數為延遲毫秒數,可選的,默認值為0.
第三個及后面的參數為函數的入參。
setTimeout 的返回值是一個數字,這個值為timeoutID,可以用于取消該定時器。
setTimeout在瀏覽器中的實現
瀏覽器渲染進程中所有運行在主線程上的任務都需要先添加到消息隊列,然后事件循環系統再按照順序執行消息隊列中的任務。
在 Chrome 中除了正常使用的消息隊列之外,還有另外一個消息隊列(我們可以稱為延遲隊列),這個隊列中維護了需要延遲執行的任務列表,包括了定時器和 Chromium 內部一些需要延遲執行的任務。所以當通過 JavaScript 創建一個定時器時,渲染進程會將該定時器的回調任務添加到延遲隊列中。
比如這樣的一段代碼:
functionfoo(){console.log("test")}vartimeoutID=setTimeout(foo,100);
當通過 JavaScript 調用 setTimeout 設置回調函數的時候,渲染進程將會創建一個回調任務,包含了回調函數foo、當前發起時間、延遲執行時間等,其模擬代碼如下所示:
structDelayTask{int64id;CallBackFunctioncbf;intstart_time;intdelay_time;};DelayTasktimerTask;timerTask.cbf=foo;timerTask.start_time=getCurrentTime();//獲取當前時間timerTask.delay_time=100;//設置延遲執行時間
創建好回調任務之后,就會將該任務添加到延遲執行隊列中。那這個回調任務,什么時候會被執行呢?
瀏覽器中有個函數是專門用來處理延遲執行任務的,暫且稱為ProcessDelayTask,它的主要邏輯如下:
voidProcessTimerTask(){//從delayed_incoming_queue中取出已經到期的定時器任務//依次執行這些任務}TaskQueuetask_queue;voidProcessTask();boolkeep_running=true;voidMainTherad(){for(;;){//執行消息隊列中的任務Tasktask=task_queue.takeTask();ProcessTask(task);//執行延遲隊列中的任務ProcessDelayTask()if(!keep_running)//如果設置了退出標志,那么直接退出線程循環break;}}
其實就是,當瀏覽器處理完消息隊列中的一個任務之后,就會開始執行 ProcessDelayTask 函數。ProcessDelayTask 函數會根據發起時間和延遲時間計算出到期的任務,然后依次執行這些到期的任務。等到期的任務執行完成之后,再繼續下一個循環過程。這樣定時器就實現了,從這個過程也可以明顯看出,定時器并不一定是準時延后執行的。
注意事項
鴻蒙官方戰略合作共建——HarmonyOS技術社區
如果當前任務執行時間過久,會延遲到期定時器任務的執行
在使用 setTimeout 的時候,有很多因素會導致回調函數執行比設定的預期值要久,其中一個就是上文說到的,如果處理的當前任務耗時過長,定時器設置的任務就會被延后執行。
比如在瀏覽器中執行這樣一段代碼,并打印執行時間:
functionbar(){console.log('bar')constendTime=Date.now()console.log('costtime',endTime-startTime)}functionfoo(){setTimeout(bar,0);for(leti=0;i<5000;i++){leti=5+8+8+8console.log(i)}}foo()
執行結果如圖:
從結果可以看到,執行 foo 函數所消耗的時長是 365 毫秒,這也就意味著通過 setTimeout 設置的任務被推遲了 365 毫秒才執行,而設置 setTimeout 的回調延遲時間是 0。
2 . 使用 setTimeout 設置的回調函數中的 this 環境不是指向回調函數
比如這段代碼:
varname=1;varMyObj={name:2,test:1,showName:function(){console.log(this.name,this.test);}}setTimeout(MyObj.showName,1000)MyObj.showName()//先輸出21//1s后輸出1undefined
這里其實認真分析一下,也很好理解這個 this 的指向。按照 this 的規定,如果是對象調用(obj.fn()),那么this指向該對象,因此MyObj.showName()輸出的是 MyObj 里面的值。在 setTimeout 中,入參是MyObj.showName,這里是把這個值傳了進去,可以理解為:
constfn=MyObj.showNamesetTimeout(fn,1000)
這樣看,在setTimeout里面,當執行到的時候,實際上就是在window下執行fn,此時的this,就指向了window,而不是原來的函數。
3 . setTimeout 存在嵌套調用問題
如果 setTimeout 存在嵌套調用,調用超過5次后,系統會設置最短執行時間間隔為 4 毫秒。
我們可以在瀏覽器粗略測試一下,有如下代碼:
letstartTime=Date.now()functioncb(){constendTime=Date.now()console.log('costtime',endTime-startTime)startTimestartTime=startTimesetTimeout(cb,0);}setTimeout(cb,0);
執行結果:
從結果可以看出,前面五次調用的時間間隔比較小,嵌套調用超過五次以上,后面每次的調用最小時間間隔是 4 毫秒(我運行的結果,間隔基本是 5ms,考慮有代碼執行的計算誤差)。
之所以出現這樣的情況,是因為在 Chrome 中,定時器被嵌套調用 5 次以上,系統會判斷該函數方法被阻塞了,如果定時器的調用時間間隔小于 4 毫秒,那么瀏覽器會將每次調用的時間間隔設置為 4 毫秒??梢钥聪略创a(https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/frame/dom_timer.cc)
staticconstintkMaxTimerNestingLevel=5;//Chromiumusesaminimumtimerintervalof4ms.We'dliketogo//lower;however,therearepoorlycodedwebsitesouttherewhichdo//createCPU-spinningloops.Using4mspreventstheCPUfrom//spinningtoobusilyandprovidesabalancebetweenCPUspinningand//thesmallestpossibleintervaltimer.staticconstexprbase::TimeDeltakMinimumInterval=base::TimeDelta::FromMilliseconds(4);
所以,一些實時性較高的需求就不太適合使用 setTimeout 了,比如你用 setTimeout 來實現 JavaScript 動畫就不一定是一個很好的主意。
4 . 未激活的頁面,setTimeout 執行最小間隔是 1000 毫秒
如果標簽不是當前的激活標簽,那么定時器最小的時間間隔是 1000 毫秒,目的是為了優化后臺頁面的加載損耗以及降低耗電量。這一點你在使用定時器的時候要注意。
5 . 延時執行時間有最大值
Chrome、Safari、Firefox 都是以 32 個 bit 來存儲延時值的,32bit 最大只能存放的數字是 2147483647 毫秒,這就意味著,如果 setTimeout 設置的延遲值大于 2147483647 毫秒(大約 24.8 天)時就會溢出,這導致定時器會被立即執行。如:
letstartTime=Date.now()functionfoo(){constendTime=Date.now()console.log('costtime',endTime-startTime)console.log("test")}vartimerID=setTimeout(foo,2147483648);//會被立即調用執行
執行結果:
運行后可以看到,這段代碼是立即被執行的。但如果將延時值修改為小于 2147483647 毫秒的某個值,那么執行時就沒有問題了。
感謝各位的閱讀,以上就是“setTimeout的實現原理及使用注意”的內容了,經過本文的學習后,相信大家對setTimeout的實現原理及使用注意這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是本站,小編將為大家推送更多相關知識點的文章,歡迎關注!
本文由 貴州做網站公司 整理發布,部分圖文來源于互聯網,如有侵權,請聯系我們刪除,謝謝!
c語言中正確的字符常量是用一對單引號將一個字符括起表示合法的字符常量。例如‘a’。數值包括整型、浮點型。整型可用十進制,八進制,十六進制。八進制前面要加0,后面...
2022年天津專場考試原定于3月19日舉行,受疫情影響確定延期,但目前延期后的考試時間推遲。 符合報名條件的考生,須在規定時間登錄招考資訊網(www.zha...
:喜歡聽,樂意看。指很受歡迎?!巴卣官Y料”喜聞樂見:[ xǐ wén lè jiàn ]詳細解釋1. 【解釋】:喜歡聽,樂意看。指很受歡迎。2. 【示例】:這是...
美國次貸危機(subprime crisis)也稱次級房貸危機,也譯為次債危機。它是指一場發生在美國,因次級抵押貸款機構破產、投資基金被迫關閉、股市劇烈震蕩引起的金融風暴。那么,美國次貸危機爆發的主要原因是什么?美國次貸危機如何解決的?一起來看看吧!美國次貸危機爆發的主要原因有:1.美國金融監管當局,特別是美聯儲貨幣政策的松緊變化。2.美國投資市場,以及全球經濟和投資環境一段時期內,情緒樂觀、持續...
(相關資料圖)哈嘍小伙伴們 ,今天給大家科普一個小知識。在日常生活中我們或多或少的都會接觸到臺式機硬盤如何接筆記本電腦方面的一些說法,有的小伙伴還不是很了解,今天就給大家詳細的介紹一下關于臺式機硬盤如何接筆記本電腦的相關內容。1、筆記本電腦一般硬盤的位置在筆記本的背面,用螺絲刀把硬盤保護蓋打開,這時筆記本硬盤后端有顆螺絲,用螺絲刀打開,取出筆記本硬盤。2、將插在臺式機上硬盤的數據線和電源取下。3、...
【資料圖】1、通過民用航空局官網查詢。2、打開中國民用航空局官網2、點擊右邊“電子客票驗真”3、輸入13位國內BSP電子客票號碼(驗真期限:使用前1年內,使用后1個月內)或輸入11位航空運輸電子客票行程單印刷序號和姓名即可。3、擴展資料:機票行程單開具注意事項回:機票行程單是旅客購買電子機票的付款報銷憑證,但不作為機場辦理乘機手續和安全檢查的必要證明。4、行程單最晚在航班起飛后7天內打印。5、2、...