天天看點

js中setTimeout和setInterval性能詳解總結

在寫h5遊戲時經常需要使用定時重新整理頁面實作動畫效果,比較常用即settimeout()以及setinterval()

settimeout() 方法用于在指定的毫秒數後調用函數或計算表達式,而setinterval()則是在每隔指定的毫秒數循環調用函數或表達式,直到clearinterval把它清除。也就是說settimeout()隻執行一次,setinterval()可以執行多次。兩個函數的參數也相同,第一個參數是要執行的code或句柄,第二個是延遲的毫秒數

timeoutid:定時器id号,它可以在cleartimeout()函數中被用來清除定時器。

code:一個被執行的代碼串或函數

millisec:延遲的時間,機關毫秒。如果沒有指定,預設為0

settimeout() 方法用于在指定的毫秒數後調用函數或計算表達式

注:調用過程中,可以使用cleartimeout(id_of_settimeout)終止

window.settimeout或settimeout,兩個寫法基本一樣,隻不過window.settimeout将settimeout函數作為全局window對象的一個屬性來引用

window.settimeout方法調用函數有兩種方法:

無論window.settimeout還是window.setinterval,在使用函數名作為調用句柄時都不能帶參數,而在許多場合必須要帶參數,這就需要想方法解決。例如對于函數hello(_name),它用于針對使用者名顯示歡迎資訊: var username="jack";

這時,如果企圖使用以下語句來使hello函數延遲3秒執行是不可行的:

這将使hello函數立即執行,并将傳回值作為調用句柄傳遞給settimeout函數,其結果并不是程式需要的。而使用字元串形式可以達到想要的結果:

如果在延時期限到達之前取消示範執行,可以使用window.cleartimeout(timeoutid)方法

這樣,如果要取消顯示,隻需單擊頁面任何一部分,就執行了window.cleartimeout方法,使得逾時操作被取消

除了前兩個參數,settimeout還允許添加更多的參數。它們将被傳入推遲執行的函數

上面代碼中,settimeout共有4個參數。最後兩個參數,将在1000毫秒之後回調函數執行時,作為回調函數的參數。

ie 9.0以下版本,隻允許settimeout有兩個參數。這時有三種解決方法,第一種是自定義settimeout,使用apply方法将參數輸入回調函數;第二種是在一個匿名函數裡面,讓回調函數帶參數運作,再把匿名函數輸入settimeout;第三種使用bind方法,把多餘的參數綁定在回調函數上面,生成一個新的函數輸入settimeout

除了參數問題,settimeout還有一個需要注意的地方:被settimeout推遲執行的回調函數是在全局環境執行,這有可能不同于函數定義時的上下文環境

上面代碼輸出的是1,而不是2,這表示回調函數的運作環境已經變成了全局環境

再看一個不容易發現錯誤的例子

上面代碼隻會顯示undefined,因為等到user.sayhi執行時,它是在全局對象中執行,是以this.login取不到值。為了防止出現這個問題,一種解決方法是将user.sayhi放在函數中執行

user.sayhi是在函數作用域内執行,而不是在全局作用域内執行,是以能夠顯示正确的值

另一種更通用的解決方法,則是采用閉包,将this與目前運作環境綁定

上面代碼中,settimeout指定的函數中的this,總是指向定義時所在的dom節點

setinterval函數的參數及用法和settimeout函數一樣,不同的是,setinterval() 方法可按照指定的周期(以毫秒計)來調用函數或計算表達式

我們定義一個定時器timer,使用setinterval()每隔1秒調用一次timego()。這樣timego會執行10次,每次數字tt會減1,直到為0。那麼如果想停止定時器,可以使用以下代碼:

與settimeout一樣,除了前兩個參數,setinterval方法還可以接受更多的參數,它們會傳入回調函數

setinterval指定的是,“開始執行”之間的間隔,是以實際上,兩次執行之間的間隔會小于setinterval指定的時間。假定setinterval指定,每100毫秒執行一次,每次執行需要5毫秒,那麼第一次執行結束後95毫秒,第二次執行就會開始。如果某次執行耗時特别長,比如需要105毫秒,那麼它結束後,下一次執行就會立即開始

上面代碼每隔2000毫秒跳出一個alert對話框。如果使用者一直不點選“确定”,整個浏覽器就處于“堵塞”狀态,後面的執行就一直無法觸發,會累積起來。舉例來說,第一次跳出alert對話框後,使用者過了6000毫秒才點選“确定”,那麼第二次、第三次、第四次執行将累積起來,它們之間不會再有等待間隔。

為了確定兩次執行之間有固定的間隔,可以不用setinterval,而是每次執行結束後,使用settimeout指定下一次執行的具體時間。上面代碼用settimeout,可以改寫如下

<code>cleartimeout(對象)</code> 清除已設定的<code>settimeout</code>對象;<code>clearinterval(對象)</code> 清除已設定的<code>setinterval</code>對象

其實settimeout()也可以實作每隔一段時間重複執行某個函數,是以在js中settimeout 和 setinterval 都經常被用來做網頁上的定時器,允許為它指定一個毫秒數作為間隔執行的時間。當被啟動的程式需要在非常短的時間内運作,我們就會給她指定一個很小的時間數,或者需要馬上執行的話,我們甚至把這個毫秒數設定為0,但事實上,settimeout有一個最小執行時間,當指定的時間小于該時間時,浏覽器會用最小允許的時間作為settimeout的時間間隔,也就是說即使我們把settimeout的毫秒數設定為0,被調用的程式也沒有馬上啟動

很多人會理所當然的認為for循環會分别列印出1,2,3. 但是事實不是這樣的,會輸出3次4. 要了解為什麼會列印三次4,我們先來了解settimeout這個函數吧,很多人會認為上面的settimeout的意思是這樣的:在100毫秒後執行settimeout的回調函數,其實這樣的了解是有誤的,其實settimeout與setinterval真正的含義如下:

settimeout:在指定的毫秒數後,将定時任務處理的函數添加到執行隊列的隊尾

setinterval:按照指定的周期(以毫秒數計時),将定時任務處理函數添加到執行隊列的隊尾

settimeout與setinterval都是異步的,是以我們現在可以來了解下上面循環為什麼一直都是4呢?其實調用settimeout時候,會有一個延時事件排入隊列,然後settimeout調用之後的那行代碼運作,接着是再下一行代碼,直到再也沒有任何代碼了,javascript虛拟機才會問,隊列裡還有嗎?如果隊列中至少有一個事件适合于觸發,比如上面的settimeout函數,則會調用settimeout那個函數。是以上面的代碼先for循環,循環結束,而 i === 4一直遞增,直到不再滿足i&lt;=3為止。是以就列印了3個4

運作結果是:先列印出 “我是新來的,我先執行”這句代碼,接着列印”列印我,我是異步執行的”代碼

settimeout的作用是将代碼推遲到指定時間執行,如果指定時間為0,即settimeout(f,0),那麼會立刻執行嗎?

答案是不會。因為上一段說過,必須要等到目前腳本的同步任務和“任務隊列”中已有的事件,全部處理完以後,才會執行settimeout指定的任務。也就是說,settimeout的真正作用是,在“任務隊列”的現有事件的後面再添加一個事件,規定在指定時間執行某段代碼。settimeout添加的事件,會在下一次event loop執行

settimeout(f,0)将第二個參數設為0,作用是讓f在現有的任務(腳本的同步任務和“任務隊列”中已有的事件)一結束就立刻執行。也就是說,settimeout(f,0)的作用是,盡可能早地執行指定的任務

上面代碼說明,settimeout(f,0)必須要等到目前腳本的所有同步任務結束後才會執行。

0毫秒實際上達不到的。根據html 5标準,settimeout推遲執行的時間,最少是4毫秒。如果小于這個值,會被自動增加到4。另一方面,浏覽器内部使用32位帶符号的整數,來儲存推遲執行的時間。這意味着settimeout最多隻能推遲執行2147483647毫秒(24.8天),超過這個時間會發生溢出,導緻回調函數将在目前任務隊列結束後立即執行,即等同于settimeout(f,0)的效果

比如,網頁開發中,某個事件先發生在子元素,然後冒泡到父元素,即子元素的事件回調函數,會早于父元素的事件回調函數觸發。如果,我們先讓父元素的事件回調函數先發生,就要用到settimeout(f, 0)

面代碼在點選按鈕後,先觸發回調函數a,然後觸發函數c。在函數a中,settimeout将函數b推遲到下一輪loop執行,這樣就起到了,先觸發父元素的回調函數c的目的了

使用者自定義的回調函數,通常在浏覽器的預設動作之前觸發。比如,使用者在輸入框輸入文本,keypress事件會在浏覽器接收文本之前觸發。是以,下面的回調函數是達不到目的的

上面代碼想在使用者輸入文本後,立即将字元轉為大寫。但是實際上,它隻能将上一個字元轉為大寫,因為浏覽器此時還沒接收到文本,是以this.value取不到最新輸入的那個字元。隻有用settimeout改寫,上面的代碼才能發揮作用

上面代碼将代碼放入settimeout之中,就能使得它在浏覽器接收到文本之後觸發

由于settimeout(f,0)實際上意味着,将任務放到浏覽器最早可得的空閑時段執行,是以那些計算量大、耗時長的任務,常常會被放到幾個小部分,分别放到settimeout(f,0)裡面執行

上面代碼有兩種寫法,都是改變一個網頁元素的背景色。寫法一會造成浏覽器“堵塞”,而寫法二就能就不會,這就是settimeout(f,0)的好處

另一個使用這種技巧的例子是,代碼高亮的處理。如果代碼塊很大,就會分成一個個小塊,寫成諸如settimeout(highlightnext, 50)的樣子,進行分塊處理

繼續閱讀