setTimeout(0)


由 John Resig 的 How JavaScript Timers Work 可以知道,現有的 JavaScript 引擎是單線程處理任務的。它把任務放到隊列中,不會同步去執行,必須在完成一個任務后才開始另外一個任務。

讓我們看看我之前的文章:JavaScript的9個陷阱及評點,在第 9 點 Focus Pocus 中提到的問題。原作者對這個認識有所偏差,其實不只是 IE 的問題,而是現有 JavaScript 引擎對於線程實現的問題(關於線程,我的概念其實不多,如果不對,希望讀者多多指教)。我們通過一個例子來說明,請訪問http://realazy.org/lab/settimeout.html. 我們來看 1 和 2。如果你能看看源代碼,會發現我們的任務很簡單,就是給文檔增加一個 input 文本框,並聚焦和選中。請現在分別點擊一下,可以看到,1 並沒有能夠聚焦和選中,而 2 可以。它們之間的區別在於,在執行:

input.focus();
input.select();

時, 2 多了一個延遲時間為 0 的 setTimeout 的外圍函數,即:

setTimeout(function(){
 input.focus();
 input.select();
}, 0);

按照 JavaScript: The Definitive Guide 5th 的 14.1 所說:

在實踐中,setTimeout 會在其完成當前任何延宕事件的事件處理器的執行,以及完成文檔當前狀態更新后,告訴瀏覽器去啟用 setTimeout 內注冊的函數。

其實,這是一個把需要執行的任務從隊列中跳脫的技巧。回到前面的例子,JavaScript 引擎在執行 onkeypress 時,由於沒有多線程的同步執行,不可能同時去處理剛創建元素的 focus 和 select 事件,由於這兩個事件都不在隊列中,在完成 onkeypress 后,JavaScript 引擎已經丟棄了這兩個事件,正如你看到的例子 1 的情況。而在例子 2 中,由於setTimeout可以把任務從某個隊列中跳脫成為新隊列,因而能夠得到期望的結果。

這才是延遲事件為 0 的setTimeout的真正目的。在此,你可以看看例子 3,它的任務是實時更新輸入的文本,現在請試試,你會發現預覽區域總是落后一拍,比如你輸 a, 預覽區並沒有出現 a, 在緊接輸入 b 時, a 才不慌不忙地出現。其實我們是有辦法讓預覽區跟輸入框同步地,在此我沒有給出答案,因為上面所說的,就是解決思路,try it yourself!

-----------------------------------------------------------------------------------------------------------------------------------

相信所有做前端開發的同學都會經常使用 setTimeout(0) 來做很多事情,這個一度成為解決了很多前端疑難雜症的法寶。而且大家也知道 setTImeout(0) 的極限在16ms左右。也許很多人沒想到這個是一個很嚴重的問題,但是在w3c性能小組的專家眼里這是一個非常糾結的設置。

那setTimeout和16ms會帶來什么問題呢?

1. 我們都知道,我們在做頁面動畫的時候大多數都是在用setTImeout來控制每一幀的動畫的, 而多個setTimeout的存在會導致很多次CPU中斷調度的開銷,為了減少這些開銷,我們希望同一個統一的CPU中斷調度管理調度單元來管理所有動畫,

於是就出現了 requestAnimationFrame。 requestAnimationFrame 的出現不僅僅可以解決中斷調度的問題,還可以更加優化得統一管理動畫單元里dom元素的repaint方式。

2. 杯具的16ms,在早期。js的callback執行,是依賴CPU的中斷來進行控制的,如果兩個中斷之間時間太短會導致,CPU性能消耗很高,同時影響能耗,於是微軟和英特公司為了解決這個問題,就約定每個中斷之間的間隔是15.6ms(64 fps)所以就是我們常見的約等於16ms的間隔。不過隨着web的要求不斷增加,大家對這個要求希望是放寬的態度,於是在高端瀏覽器,這個性能被提升了4倍左右,所以在chrome,ie10等瀏覽器,setTimeout的間隔縮短到了 4ms (250 fps)。 但是問題來了,這么高的消耗以傳統的方式,並不能從根本上解決CPU的調度問題,而且能耗也會提升40%

於是W3C,提出需要取代 setTimeout(0) 的代替品 --- setImmediate

不過到底底層如何解決了這個CPU調度,還能保證腳本能夠很快執行,這塊舜子還在研究,了解的朋友也歡迎一起交流哈。

 

這是兩個非常好的方法用來取代setTimeout的api,舜子也做了一些小小的實驗來驗證 requestAnimationFrame,setImmediate 和 setTimeout 之間的執行效率區別,在IE10下可以看到,setImmediate 接口的callback次數可以達到每秒 6000 次的的執行。而requestAnimationFrame主要目的是為了保證動畫的圓滑播放,所以基本上是控制在60 fps的范圍,而且根據文檔介紹,只是一個時鍾控制器在進行調度,而且會更加需要來進行按需渲染。

http://www.pjhome.net/web/html5/timing_test.htm

轉自:http://www.pjhome.net/article/Javascript/setImmediate_requestAnimationFrame.html

--------------------------------------------------------------------------------------------------------------------------------------

body { background:url(images/page-bg.gif); font-size:14px;}

setTimeout為0的應用

在下面兩個輸入框里輸入字符,看看有什么不同?

A:沒有使用setTimeout

你輸入了:

B:setTimeout為0

你輸入了:
<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>  
< html  xmlns ="http://www.w3.org/1999/xhtml" >  
< head >  
< meta  http-equiv ="Content-Type"  content ="text/html; charset=gb2312"   />  
< title > setTimeout為0的應用 </ title >  
< style  type ="text/css" >  
body 
{  background : url(images/page-bg.gif) ;  font-size : 14px ; }  
</ style >  
</ head >  

< body >  
< h1 > setTimeout為0的應用 </ h1 >  
< p > 在下面兩個輸入框里輸入字符,看看有什么不同? </ p >  
< p >  
A:
< input  type ="text"  id ="t1"  name ="t1"  
onkeydown
="document.getElementById('t1text').innerHTML='你輸入了:'+this.value;"  value =""   /> 沒有使用setTimeout 

</ P >  
< div  id ="t1text" > 你輸入了: </ div >  
< p >  
B:
< input  type ="text"  id ="t2"  name ="t2"  
onkeydown
="var t2=this;setTimeout(function(){document.getElementById('t2text').innerHTML='你輸入了:'+t2.value},0);"  
value
=""   /> setTimeout為0 </ p >  
< div  id ="t2text" > 你輸入了: </ div >  
</ body >  
</ html >



小結: 

類似起線程。前者沒用settimeout ,所以是主線程去做的響應,所以在onkeydown 就觸發事件執行。得到的數據會有差異。

-------------------------------------------------------------------------------------------------------------------------------------------

經常看到setTimeout延時0ms的javascript代碼,感到很迷惑,難道延時0ms和不延時不是一個道理嗎?后來通過查資料以及實驗得出以下兩個作用,可能還有作用我還不知道,希望得知的朋友在后面評論上不吝指出。
1、實現javascript的異步;
正常情況下javascript都是按照順序執行的。但是我們可能讓該語句后面的語句執行完再執行本身,這時就可以用到setTimeout延時0ms來實現了。
如: 
alert(1); 
setTimeout("alert(2)", 0); 
alert(3); 
雖然延時了0ms,但是執行順序為:1,3,2 
這樣就保證setTimeout里面的語句在某一代碼段中最后執行。 


2、在事件中,setTimeout 會在其完成當前任何延宕事件的事件處理器的執行,以及完成文檔當前狀態更新后,告訴瀏覽器去啟用 setTimeout 內注冊的函數。
 舉個例子來說這句話的意思,假如當某個事件在頁面上建立一個文本框,並給文本框賦值(完成文檔當前狀態更新),然后將焦點定到文本框,並且選中文本框的內容(后面部分就需要用到setTimeout 延遲0ms實現,否則不好實現)。
先看個例子:

復制代碼
 1 <! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd" >
 2 < html >
 3 < head >
 4 < title > setTimeout </ title >
 5 < script  type ="text/javascript"   >
 6 (function(){
 7  
 8  function get(id){
 9   return document.getElementById(id);
10  }

11  
12  window.onload = function(){
13   get('makeinput').onmousedown = function(){
14    var input = document.createElement('input');
15    input.setAttribute('type''text');
16    input.setAttribute('value''test1');
17    get('inpwrapper').appendChild(input);
18    input.focus();
19    input.select();
20   }

21   get('makeinput2').onmousedown = function(){
22    var input = document.createElement('input');
23    input.setAttribute('type''text');
24    input.setAttribute('value''test1');
25    get('inpwrapper2').appendChild(input);
26    setTimeout(function(){
27     input.focus();
28     input.select();
29    }
0);
30   }

31   get('input1').onkeypress = function(){
32    get('preview1').innerHTML = this.value;
33   }

34   get('input2').onkeypress = function(){
35                                setTimeout(function(){
36    get('preview2').innerHTML = get('input2').value;
37                               }
,0 );
38   }

39  }

40 }
)();
41
</ script >
42 </ head >
43 < body >
44   < h1 >< code > DEMO1 </ code ></ h1 >
45   < h2 > 1、未使用  < code > setTimeout </ code > (未選中文本框內容) </ h2 >
46   < button  id ="makeinput" > 生成 input </ button >
47   < id ="inpwrapper" ></ p >
48   < h2 > 2、使用  < code > setTimeout </ code > (立即選中文本框內容) </ h2 >
49   < button  id ="makeinput2" > 生成 input </ button ></ h2 >
50   < id ="inpwrapper2" ></ p >
51
52 --------------------------------------------------------------------------
53   < h1 >< code > DEMO2 </ code ></ h1 >
54   < h2 > 1、未使用  < code > setTimeout </ code > (只有輸入第二個字符時,前一個字符才顯示出來) </ h2 >
55   < input  type ="text"  id ="input1"  value ="" />< div  id ="preview1" ></ div >
56   < h2 > 2、使用  < code > setTimeout </ code > (輸入時,字符同時顯示出來) </ h2 >
57 < input  type ="text"  id ="input2"  value ="" />< div  id ="preview2" ></ div >
58 </ body >
59 </ html >
60
61
復制代碼

 

運行示例
現有的 JavaScript 引擎是單線程處理任務的。它把任務放到隊列中,不會同步去執行,必須在完成一個任務后才開始另外一個任務。其實,這是一個把需要執行的任務從隊列中跳脫的技巧。在DEMO1中,JavaScript 引擎在執行 onmousedown時,由於沒有多線程的同步執行,不可能同時去處理剛創建元素的 focus 和 select 方法,由於這兩個方法都不在隊列中,在完成 onmousedown 后,JavaScript 引擎已經丟棄了這兩個任務,正如第一種情況。而在第二種情況中,由於setTimeout可以把任務從某個隊列中跳脫成為新隊列,因而能夠得到期望的結果。

轉自:http://www.cnblogs.com/xieex/archive/2008/07/11/1241137.html

另:

http://stackoverflow.com/questions/4574940/settimeout-with-zero-delay-used-often-in-web-pages-why

http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM