setTimeout你知多少

假期這麼快就結束了,其實對我來講沒什麼影響,由於我一週才兩節課,對於課多的同窗來講,我每天在休假,不要羨慕喲~  但休假並不表明閒着,仍是得苦逼的編代碼,唉。。一入程序深似海。。ajax

無論學得多少,仍是總結一些,仍是一些小問題。固然也是很重要的,好! 廢話少說該入正題了。瀏覽器

上次提到異步,當時說,不知道是啥就去查漢語字典,但後來發現查了字典仍是不會。回顧一下併發

js哪些操做是異步的???setTimeout、setInterval、ajax、各類事件處理,才疏學淺,我就知道這些,誰還知道有哪些,勞煩告訴我,學習學習。dom

for(var i=0; i<5; i++){
   setTimeout(function(){
     console.log(i);
   },100);
}   //答案是: 5 5 5 5

for(var i=0; i<5; i++){
    (function(j){
       setTimeout(function(){
          console.log(j);
       },100);
    })(i);  
};   答案是 0 1 2 3 4

爲何是這個答案,重申一遍:做用域的關係。具體解釋做用域與執行環境無關,由定義時決定並一步一步往上查找。上述兩個例子 執行匿名函數時執行的是:異步

function(){
     console.log(i);
   }  而i等於多少? 咱們從定義處查找 setTimeout中沒有i  在往上一層就到了全局中,此時i已經等於5  因此答案是全是5
function(){
     console.log(j);
   }  而j等於多少?  咱們仍是從定義處查找 setTimeout中沒有j  在往上一層就到了上一個形參爲j的匿名函數,此時j是形參,在定義setTimeout中的函數時,j的形參依次被傳入實參i,依次爲0,1,2,3,4  因此答案是全是0,1,2,3,4

換湯不換藥,找個例子實驗一下:函數

[1,2,3,4,5].forEach(function(elem){
    setTimeout(function(){console.log(elem)}, 200);
})  答案是多少??  5,5,5,5??  1,1,1,1??  1,2,3,4,5仍是???  答案是1,2,3,4,5  若是錯了的話,再把前面的例子,文字看看。 

繼續,再來一道:學習

for(var i=0; i<5; i++){
   (function(j){
      setTimeout(function(){
          console.log(j);
      }, Math.random()*1000);
   })(i);
} //這個答案是什麼呢??  是0,1,2,3,4仍是什麼???   好好想一想。

根據前面的分析,先找定義處 依次往上查找,到function(j)這個函數時,已經把實參i傳進來了,因此答案是0,1,2,3,4  yes or no?? 答案是錯誤的。爲何?this

實參i確實把值傳進來了,該段代碼就等價於spa

setTimeout(function(){
          console.log(0);
      }, Math.random()*1000);

setTimeout(function(){
          console.log(1);
      }, Math.random()*1000);

setTimeout(function(){
          console.log(2);
      }, Math.random()*1000);

setTimeout(function(){
          console.log(3);
      }, Math.random()*1000);

setTimeout(function(){
          console.log(4);
      }, Math.random()*1000);   

此時只看這段代碼 答案是多少??? 你們確定會說是亂序的,跟 (Math.random()*1000) 值有關,yes 你答對了。 因此上面那個答案是亂序的0,1,2,3,4

那麼,下面那個代碼呢?設計

setTimeout(function(){
   console.log(15);
},100);
setTimeout(function(){
   console.log(5);
},200);  

不少人確定會說,這還用說嗎? 不用想都知道是15, 5。對,可是就這段代碼而言,這個15,5   從等待到執行(此處執行時間忽略不計)一共是花了300ms仍是200ms呢? 答案是200ms,爲何?剛剛開頭就說過,setTimeout()函數是異步的,異步有個的特色就是併發性,在同時定義這兩個函數時,他們同時在等待,放入到消息隊列中,因此100ms後第一個函數放入時,第二個函數已經等了100ms,因此兩個函數一共等了200ms。 總之一句話:異步具備併發性,與順序無關(時間相同或者相近的狀況下有關),與時間的快慢有關,請記住它。

這裏還要提的是:關於定時器中的時間,指的是什麼時候將定時器的代碼添加到隊列中,而不是什麼時候實際執行代碼,只能表示它會盡快執行。

如 :

document.onclick = function(){
  setTimeout(function(){
          console.log(34);
      },250); 
};  //若是onclick事件處理程序執行了300ms 那麼定時器代碼至少要在定時器設置後的300ms纔會被執行,也就是34至少要在300ms後輸出。

 你們立刻就想到,若是是這樣的話,setInterval()就會出現一種狀況:在代碼再次被添加到隊列以前沒完成執行,致使定時器代碼連續運行好幾回,沒有停頓。幸虧,js引擎夠聰明,能避免這個問題,如何避免?當使用setInterval()時,僅當沒有該定時器的任何其餘代碼實例時,纔將定時器代碼添加到隊列中,這樣確保了定時器代碼加入到隊列中的最小時間間隔爲指定間隔,注意是添加到隊列中的最小時間間隔而不是執行。可是。。這個規則有兩個問題:1.某些間隔會被跳過,2.多個定時器的代碼執行之間的間隔可能會比預期的少。舉個例子:

某個onclick事件處理程序使用setInterval()設置了一個200ms間隔的重複定時器,若是事件程序花了300ms多一點的時間完成,定時器也花差很少的時間,就會出現上述兩個問題。

如圖:   此圖來自《js高級程序設計》這本書強烈推薦閱讀。。。

咱們分析一下:在5ms時建立了間隔爲200ms的定時器,第一個定時器在205ms後被添加到隊列中,但直到onclik執行完才執行,執行時,在405ms處第二個定時器又被添加到隊列中,在605ms第三個定時器要添加到隊列中,但此時第二個定時器還沒被執行,因此第三個不會被添加,同時在第一個定時器執行完以後第二個當即執行,因此第一個定時器和第二個定時器執行的間隔小於200ms,其實此處就是從第一個執行結束到第二個開始執行沒有間隔。

有人可能想到這樣的話js引擎並無解決定時器代碼連續運行問題,確實,但其實js引擎這種作法(在僅當沒有該定時器的任何其餘代碼實例時,纔將定時器代碼添加到隊列中),減小了連續的次數,不至於堆積太多。

爲了不這2個缺點,可使用以下模式使用鏈式setTimeout()調用。

setTimeout(function(){
   //處理中
   setTimeout(arguments.callee,interval); //arguments.callee  獲取對當前函數執行的引用。  

//此處把須要處理的代碼寫在前面 有一個好處是 :下一個定時器必定是在前一個將要結束(此處能夠之直接視爲結束)才定義
}, interval);

每次函數執行時建立一個新的定時器,這樣的好處:在前一個定時器代碼執行完以前,不會向隊列插入新的定時器代碼,確保不會有任何缺失的間隔,並且,它能夠保證在下一次定時器代碼執行以前,至少要等待指定的間隔,避免了連續的運行。詳細請看《js高級程序設計》這本書。

 關於setTimeout()函數還有一點就是:

你們都知道DOM比非DOM交互要更多的內存和CPU時間,若是連續進行過多的DOM相關操做可能會致使瀏覽器掛起甚至崩潰。如resize事件,爲了繞開這個問題咱們可使用setTimeout();

模式以下:

var processor = {
    timeoutId: null,
    performProcessing: function(){
      //實際執行代碼 
   },
   process: function(){
clearTimeout(this.timeoutId);
var that = this; //保存this,由於setTimeout()中用到的函數環境老是window
this.timeoutId = setTimeout(function(){ //timeoutId用來保存本次setTimeout的id以便下一次調用時清除
that.performProcessing();
}, 100);
}
};

processor.process();

時間間隔設爲100ms,表示最後一次調用process()以後至少100ms後纔會被調用performProcessing(),若是100ms以內調用了process()共20次,performProcessing()仍只會被調用一次。由於,在100ms以內定時器都沒開始執行,調用process()只會清除前一次的,最後只剩下最後一次setTimeout()。也就是說performProcessing()仍只會被調用一次。

這個過程叫作函數節流,基本思想是:某個代碼不能夠在沒有間斷的狀況連續重複執行。今天先暫且消化這些。

哪不對,或者要補充,推薦的強烈歡迎。。。

相關文章
相關標籤/搜索