時間延遲函數詳解

js是單線程語言,但它容許經過設置超時值和間歇時間值來調度代碼在特定的時刻執行。setTimeout是在指定delay時間後,將指定方法做爲異步任務添加到異步任務隊列中,而setInterval則是能夠循環地每隔一個delay就向異步任務隊列中添加一個任務javascript

語法

setTimeout

// 返回惟一的ID
let timeoutID = window.setTimeout(func[, delay, param1, param2, ...]);
let timeoutID = window.setTimeout(code[, delay]);

setInterval

// 返回惟一的ID
let intervalID = window.setInterval(func, delay[, param1, param2, ...]);
let intervalID = window.setInterval(code, delay);

clearTimeout和clearInterval

window.clearTimeout(timeoutID)
window.clearInterval(intervalID)
  • timeoutID/intervalID 能夠用來做爲clearTimeout/clearInterval方法的參數html

  • func是在delay毫秒以後執行的函數java

  • code 在第二種語法,是指你想要在delay毫秒以後執行的代碼字符串 (使用該語法是不推薦的, 不推薦的緣由和eval()同樣)web

  • delay 是延遲的毫秒數 (一秒等於1000毫秒),函數的調用會在該延遲以後發生。若是省略該參數,delay取默認值0。實際的延遲時間可能會比 delay 值長,緣由看下面的介紹。此外,根據w3c文檔得知,當delay小於0的時候,delay按0來計算chrome

setTimeout 和setInterval的使用方法相似,重點以setTimeout爲例,有差別的地方再單獨分析。瀏覽器

例子

一、比較一下下面兩個函數的區別框架

setTimeout(function () {
  func1()
}, 0)

setTimeout(function () {
  func1()
})

0秒延遲,此回調將會放到一個能當即執行的時段進行觸發。javascript代碼大致上是自頂向下的,但中間穿插着有關DOM渲染,事件迴應等異步代碼,他們將組成一個隊列,零秒延遲將會實現插隊操做。不寫第二個參數,瀏覽器自動配置時間,在IE,FireFox中,第一次配可能給個很大的數字,100ms上下,日後會縮小到最小時間間隔,Safari,chrome,opera則多爲10ms上下。
---出自《javascript框架設計》異步

若是對0s的例子不明白,沒關係,咱們繼續看下面這個:ide

var arr = [1,2,3];
for(var i in arr){
  setTimeout(function(){console.log(arr[i])},0);
  console.log(arr[i]);
}

控制檯會打印什麼?函數

1
 2
 3
 3  //3次

雖然setTimeout函數在每次循環的開始就調用了,可是卻被放到循環結束才執行,循環結束,i=3,接着連續打印了3次3。
這裏涉及到了js單線程執行的問題:js在瀏覽器中是單線程執行的,必須在完成當前任務後才執行隊列中的下一個任務。此外對於js還維護着一個setTimeout隊列,未執行的setTimeout任務就按前後順序排在隊列中,等到主線程的普通任務隊列執行完後,主線程就按順序執行積累在setTimeout中的任務。因此上面的問題會先打印一、二、3.最後纔打印3個3。

此時就有個疑問了,設置delay爲0,而實際是大於0才執行的,何須呢?
實際上這裏咱們主要用來改變任務的執行順序,由於瀏覽器會在執行完當前任務隊列中的任務,再執行setTimeout隊列中積累的任務。經過設置任務在延遲到0s後執行,就能改變任務執行的前後順序,延遲該任務發生,使之異步執行。

二、delay後能夠傳遞第三個及多個參數。

for (var i = 1; i < 4; i++) {
  var t = setTimeout(function(i) {
    console.log(i); // 2\. 1  4.2
    console.log(t); // 3\. 3  5.3
    clearTimeout(t);
  }, 10, i);
}
console.log(i);

控制檯會打印什麼?

4
 1
 3
 2
 3

此處在每次循環中,都將i做爲回調函數的參數i傳入,第一次傳入1,第二次傳入2,第三次傳入3。第四次不知足循環條件,跳出循環,而後執行console.log(i)。主線程執行完當前任務隊裏的任務,而後開始執行seTimeout的隊列。此時t爲setTimeout最後的返回ID值3,則一次打印1,3 2,3。由於clearTimeout清楚了id爲3的延時任務,因此只執行了兩次。故一次打印41323
僅IE不支持第三個及以上參數

三、關於this指向的問題

myArray = ["zero", "one", "two"];
myArray.myMethod = function (sProperty) {
    alert(arguments.length > 0 ? this[sProperty] : this);
};

myArray.myMethod(); // prints "zero,one,two"
myArray.myMethod(1); // prints "one"
setTimeout(myArray.myMethod, 1000); // prints "[object Window]" after 1 second
setTimeout(myArray.myMethod, 1500, "1"); // prints "undefined" after 1,5 seconds

由setTimeout()調用的代碼運行在與所在函數徹底分離的執行環境上. 這會致使,這些代碼中包含的 this 關鍵字會指向 window (或全局)對象,這和所指望的this的值是不同的

如何解決this指向不肯定的問題,咱們能夠用Function.prototype.bind()方法。

myArray = ["zero", "one", "two"];
myBoundMethod = (function (sProperty) {
    console.log(arguments.length > 0 ? this[sProperty] : this);
}).bind(myArray);

myBoundMethod(); // prints "zero,one,two" because 'this' is bound to myArray in the function
myBoundMethod(1); // prints "one"
setTimeout(myBoundMethod, 1000); // still prints "zero,one,two" after 1 second because of the binding
setTimeout(myBoundMethod, 1500, "1"); // prints "one" after 1.5 seconds

應用

一、替換setInterval來實現重複定時
二、防止事件瘋狂觸發
三、IE下從新播放單次gif動畫
四、blur事件延時生效

參考資料

[https://html.spec.whatwg.org/...
[http://imweb.io/topic/56ac67f...
[https://developer.mozilla.org...
[http://www.cnblogs.com/suspid...
[https://johnresig.com/blog/ho...
[http://www.cnblogs.com/snandy...
[http://www.ruanyifeng.com/blo...
[http://www.cnblogs.com/silin6...

文章地址:[http://17biu.cn/2017/07/18/se...

相關文章
相關標籤/搜索