WindowOrWorkerGlobalScope接口方法:setTimeout()

2018-03-29 10:28 更新

setTimeout()方法

WindowOrWorkerGlobalScope mixin 的 setTimeout() 方法  (以及 window.setTimeout 的后繼)用于設(shè)置一個(gè)定時(shí)器,該定時(shí)器在定時(shí)器到期后執(zhí)行一個(gè)函數(shù)或指定的一段代碼。

setTimeout()方法語法

var timeoutID = scope .setTimeout(function [,delay,param1,param2,...]);
var  timeoutID = scope .setTimeoutfunction [,delay ]);
var timeoutID = scope .setTimeoutcode [,delay ]);

setTimeout()方法參數(shù)

function
在計(jì)時(shí)器到期后執(zhí)行 function 
code
另一種語法允許您包含一個(gè)字符串,而不是一個(gè)函數(shù),該函數(shù)在計(jì)時(shí)器到期時(shí)編譯并執(zhí)行。不建議出于使用eval()安全風(fēng)險(xiǎn)的相同原因而使用此語法。
delay 可選的
時(shí)間(以毫秒(千分之一秒)為單位),計(jì)時(shí)器應(yīng)該在指定的功能或代碼執(zhí)行之前等待。如果省略此參數(shù),則使用值0,意味著盡快執(zhí)行“immediately”或更準(zhǔn)確地執(zhí)行。請(qǐng)注意,在任何一種情況下,實(shí)際延遲可能比預(yù)期的要長。
param1, ..., paramN 可選的
額外的參數(shù)一旦定時(shí)器到期就傳遞給由 function 或 code 指定的函數(shù)。

注意:在 Internet Explorer 9 及更低版本中,將第一種語法的附加參數(shù)傳遞給函數(shù)不起作用。如果您想在該瀏覽器上啟用此功能,則必須使用填充(polyfill),請(qǐng)參考本文的 Polyfill 部分。

setTimeout()方法返回值

setTimeout() 方法返回的 timeoutID 是一個(gè)正整數(shù)值,用于標(biāo)識(shí)由該 setTimeout() 調(diào)用創(chuàng)建的定時(shí)器;這個(gè)值可以傳遞 clearTimeout() 以取消超時(shí)。

了解 setTimeout() 和 setInterval() 共享相同的 ID 池可能會(huì)有所幫助,并且 clearTimeout() 和 clearInterval() 在技術(shù)上可以互換使用。但是,為了清晰起見,您應(yīng)該盡量始終與它們匹配以避免在維護(hù)代碼時(shí)出現(xiàn)混淆。

保證在同一對(duì)象(window 或 worker)上對(duì) setTimeout () 或 setInterval () 的后續(xù)調(diào)用將永遠(yuǎn)不會(huì)重用超時(shí) ID。但是,不同的對(duì)象使用單獨(dú)的 ID 池。

setTimeout()方法示例

以下示例在網(wǎng)頁中設(shè)置兩個(gè)簡單按鈕并將它們掛接到該例程 setTimeout() 和 clearTimeout()例程。按下第一個(gè)按鈕將設(shè)置一個(gè)超時(shí),在兩秒鐘后調(diào)用一個(gè)警報(bào)對(duì)話框,并存儲(chǔ)要使用的 clearTimeout() 超時(shí) ID。您可以選擇通過按第二個(gè)按鈕來取消該超時(shí)。

HTML內(nèi)容

<p>Live Example</p>
<button onclick="delayedAlert();">Show an alert box after two seconds</button>
<p></p>
<button onclick="clearAlert();">Cancel alert before it happens</button>

JavaScript內(nèi)容

var timeoutID;

function delayedAlert() {
  timeoutID = window.setTimeout(slowAlert, 2000);
}

function slowAlert() {
  alert('That was really slow!');
}

function clearAlert() {
  window.clearTimeout(timeoutID);
}

Polyfill(填充工具)

如果您需要將一個(gè)或多個(gè)參數(shù)傳遞給您的回調(diào)函數(shù),但需要它在不支持使用 setTimeout() 或者 setInterval() (例如 Internet Explorer 9 及以下版本)發(fā)送附加參數(shù)的瀏覽器中工作),則可以包括此填充代碼,其中啟用 HTML5 標(biāo)準(zhǔn)參數(shù)傳遞功能,只需將此代碼添加到腳本的頂部即可: 

/*\
|*|
|*|  Polyfill which enables the passage of arbitrary arguments to the
|*|  callback functions of JavaScript timers (HTML5 standard syntax).
|*|
|*|  https://developer.mozilla.org/en-US/docs/DOM/window.setInterval
|*|
|*|  Syntax:
|*|  var timeoutID = window.setTimeout(func, delay[, param1, param2, ...]);
|*|  var timeoutID = window.setTimeout(code, delay);
|*|  var intervalID = window.setInterval(func, delay[, param1, param2, ...]);
|*|  var intervalID = window.setInterval(code, delay);
|*|
\*/

(function() {
  setTimeout(function(arg1) {
    if (arg1 === 'test') {
      // feature test is passed, no need for polyfill
      return;
    }
    var __nativeST__ = window.setTimeout;
    window.setTimeout = function(vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */ ) {
      var aArgs = Array.prototype.slice.call(arguments, 2);
      return __nativeST__(vCallback instanceof Function ? function() {
        vCallback.apply(null, aArgs);
      } : vCallback, nDelay);
    };
  }, 0, 'test');

  var interval = setInterval(function(arg1) {
    clearInterval(interval);
    if (arg1 === 'test') {
      // feature test is passed, no need for polyfill
      return;
    }
    var __nativeSI__ = window.setInterval;
    window.setInterval = function(vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */ ) {
      var aArgs = Array.prototype.slice.call(arguments, 2);
      return __nativeSI__(vCallback instanceof Function ? function() {
        vCallback.apply(null, aArgs);
      } : vCallback, nDelay);
    };
  }, 0, 'test');
}())

IE只修復(fù)

如果您希望對(duì)其他移動(dòng)瀏覽器或桌面瀏覽器(包括 IE 9 及以下版本)進(jìn)行完全不引人注意的攻擊,則可以使用 JavaScript 條件注釋:

/*@cc_on
  // conditional IE < 9 only fix
  @if (@_jscript_version <= 9)
  (function(f){
     window.setTimeout = f(window.setTimeout);
     window.setInterval = f(window.setInterval);
  })(function(f){return function(c,t){var a=[].slice.call(arguments,2);return f(function(){c instanceof Function?c.apply(this,a):eval(c)},t)}});
  @end
@*/

或者根據(jù) IE 的 HTML 條件功能去做一個(gè)非常干凈的方法:

<!--[if lte IE 9]><script>
(function(f){
window.setTimeout=f(window.setTimeout);
window.setInterval=f(window.setInterval);
})(function(f){return function(c,t){
var a=[].slice.call(arguments,2);return f(function(){c instanceof Function?c.apply(this,a):eval(c)},t)}
});
</script><![endif]-->

解決方法

另一種可能是使用 anonymous 函數(shù)來調(diào)用你的回調(diào)函數(shù),但是這個(gè)解決方案有點(diǎn)消耗,例如:

var intervalID = setTimeout(function() { myFunc('one', 'two', 'three'); }, 1000);

上面的例子也可以在 arrow 函數(shù)的幫助下編寫:

var intervalID = setTimeout(() => { myFunc('one', 'two', 'three'); }, 1000);

另一種可能性是使用函數(shù)的 bind,例如:

setTimeout(function(arg1){}.bind(undefined, 10), 1000);

“this”問題

當(dāng)你傳遞一個(gè)方法給 setTimeout()(或任何其他函數(shù))時(shí),它會(huì)被一個(gè) this 值調(diào)用,這可能與你的期望不同的。

說明

由 setTimeout() 執(zhí)行的代碼是從調(diào)用 setTimeout 的函數(shù)分開的執(zhí)行上下文中調(diào)用。通常 this 為被調(diào)用函數(shù)設(shè)置關(guān)鍵字的規(guī)則適用,如果您未在調(diào)用中設(shè)置 this 或使用 bind,在非嚴(yán)格模式它將默認(rèn)為 global(或 window)對(duì)象,或者在嚴(yán)格模式下未定義。它將與調(diào)用 setTimeout 的函數(shù)的 this 值不相同。

注意:即使在嚴(yán)格模式下,回調(diào)的默認(rèn) this 值 setTimeout 仍然是 window 對(duì)象,而不是 undefined。

看下面的例子:

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"

上面的工作是因?yàn)楫?dāng) myMethod 被調(diào)用時(shí),通過調(diào)用,this 被設(shè)置為 myArray,所以在該函數(shù)內(nèi),this[sProperty] 相當(dāng)于 myArray[sProperty]。但是,在以下內(nèi)容中:

setTimeout(myArray.myMethod, 1000); // prints "[object Window]" after 1 second
setTimeout(myArray.myMethod, 1500, '1'); // prints "undefined" after 1.5 seconds

該 myArray.myMethod 函數(shù)被傳遞給 setTimeout它,然后當(dāng)它被調(diào)用時(shí),this 沒有被設(shè)置,所以它默認(rèn)為該 window 對(duì)象。也沒有選擇傳遞 thisArg 給setTimeout,因?yàn)樵?Array 方法中有像 forEach,reduce 等等,并且如下所示,使用 call 設(shè)置 this 也不起作用。

setTimeout.call(myArray, myArray.myMethod, 2000); // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object"
setTimeout.call(myArray, myArray.myMethod, 2500, 2); // same error

可能的解決方案

解決該問題的常用方法是使用包裝函數(shù)設(shè)置 this 為所需值的:

setTimeout(function(){myArray.myMethod()}, 2000); // prints "zero,one,two" after 2 seconds
setTimeout(function(){myArray.myMethod('1')}, 2500); // prints "one" after 2.5 seconds

arrow 函數(shù)也是一種可能的選擇:

setTimeout(() => {myArray.myMethod()}, 2000); // prints "zero,one,two" after 2 seconds
setTimeout(() => {myArray.myMethod('1')}, 2500); // prints "one" after 2.5 seconds

解決這個(gè) this 問題的另一種可能的方法是,將主機(jī) setTimeout() 和 setInterval() 全局函數(shù)替換為允許傳遞 this 對(duì)象并在使用 Function.prototype.call 的回調(diào)中設(shè)置它,例如:

// Enable setting 'this' in JavaScript timers
 
var __nativeST__ = window.setTimeout, 
    __nativeSI__ = window.setInterval;
 
window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
  var oThis = this, 
      aArgs = Array.prototype.slice.call(arguments, 2);
  return __nativeST__(vCallback instanceof Function ? function () {
    vCallback.apply(oThis, aArgs);
  } : vCallback, nDelay);
};
 
window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
  var oThis = this,
      aArgs = Array.prototype.slice.call(arguments, 2);
  return __nativeSI__(vCallback instanceof Function ? function () {
    vCallback.apply(oThis, aArgs);
  } : vCallback, nDelay);
};
注意

:這兩個(gè)替代品也將啟用 HTML5 標(biāo)準(zhǔn)的任意參數(shù)傳遞給 IE 中定時(shí)器的回調(diào)函數(shù)。所以他們也可以用作 polyfills。

新功能測試:

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

setTimeout(alert, 1500, 'Hello world!'); // the standard use of setTimeout and setInterval is preserved, but...
setTimeout.call(myArray, myArray.myMethod, 2000); // prints "zero,one,two" after 2 seconds
setTimeout.call(myArray, myArray.myMethod, 2500, 2); // prints "two" after 2.5 seconds
注意:JavaScript 1.8.5 引入了 Function.prototype.bind() 方法來將 this 所有調(diào)用的值設(shè)置為給定函數(shù)。這可以避免必須使用包裝函數(shù)在回調(diào)中設(shè)置 this 值。

使用 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

setTimeout()方法筆記

使用 clearTimeout () 取消超時(shí)。要重復(fù)調(diào)用函數(shù)(例如,每N毫秒),請(qǐng)考慮使用setInterval()。

傳遞字符串文字

傳遞一個(gè)字符串而不是一個(gè)函數(shù)給 setTimeout(),承受與使用 eval 相同的危險(xiǎn)。

// Recommended
window.setTimeout(function() {
    alert('Hello World!');
}, 500);

// Not recommended
window.setTimeout("alert('Hello World!');", 500);

傳遞給 setTimeout 的字符串在全局上下文中被計(jì)算,因此在將字符串計(jì)算為代碼時(shí),調(diào)用 setTimeout () 的上下文中的本地符號(hào)將不可用。

延誤時(shí)間超過指定時(shí)間的原因

有多種原因可能會(huì)導(dǎo)致超時(shí)時(shí)間超出預(yù)期。本節(jié)介紹最常見的原因。

超時(shí)限制為 >= 4ms

在目前使用的瀏覽器中,由于回調(diào)嵌套(嵌套層次至少達(dá)到一定深度),或者在一定數(shù)量的連續(xù)時(shí)間間隔后觸發(fā)連續(xù)調(diào)用時(shí),setTimeout()/setInterval() 調(diào)用每4ms 至少被節(jié)流一次。例如:

function cb() { f(); setTimeout(cb, 0); }
setTimeout(cb, 0);
setInterval(f, 0);

在 Chrome 和 Firefox 中,第五個(gè)連續(xù)回調(diào)調(diào)用被夾緊;Safari 在第 6 次通話中夾緊;在 Edge 中它是第三個(gè)。Gecko 在版本56中開始這樣對(duì)待 setInterval()。

從歷史上看,一些瀏覽器實(shí)施這種限制行為有點(diǎn)不同(例如 Firefox) - setInterval() 從任何地方進(jìn)行調(diào)用,或者在 setTimeout() 嵌套級(jí)別至少達(dá)到特定深度時(shí)調(diào)用嵌套。

要在現(xiàn)代瀏覽器中實(shí)現(xiàn) 0 ms 超時(shí),可以使用 window.postMessage(),如下所述。

注意:所述的最小延遲,DOM_MIN_TIMEOUT_VALUE 是 4 毫秒(存儲(chǔ)在Firefox 的首選項(xiàng)中: dom.min_timeout_value),DOM_CLAMP_TIMEOUT_NESTING_LEVEL 為 5。 

注意:4 ms 由 HTML5 規(guī)范指定,并且在 2010 年及以后發(fā)布的瀏覽器中保持一致。在(Firefox 5.0/Thunderbird 5.0/SeaMonkey 2.2)之前,嵌套超時(shí)的最小超時(shí)值為10毫秒。

非活動(dòng)選項(xiàng)卡中的超時(shí)限制為 >= 1000毫秒

為了減少后臺(tái)選項(xiàng)卡中的負(fù)載(以及相關(guān)的電池使用),在非活動(dòng)選項(xiàng)卡中,超時(shí)將被限制為每秒不超過一次(1000毫秒)觸發(fā)。

Firefox 從版本5開始實(shí)現(xiàn)這種行為(可以通過 dom.min_background_timeout_value 首選項(xiàng)調(diào)整 1000ms 常量)。Chrome 從版本11開始實(shí)施此行為(crbug.com/66078)。

Firefox 為 Android 使用15分鐘的超時(shí)值作為 Firefox 14 中錯(cuò)誤 736602 的后臺(tái)選項(xiàng)卡,并且后臺(tái)選項(xiàng)卡也可以完全卸載。

如果 Web Audio API AudioContext 正在播放聲音,則 Firefox 50 不再限制后臺(tái)選項(xiàng)卡。Firefox 51 進(jìn)一步修正了這個(gè)問題,即使沒有聲音播放,如果標(biāo)簽中的 AudioContext 存在,后臺(tái)選項(xiàng)卡也不再被限制。這些解決了使用基于筆記的音樂的應(yīng)用程序在選項(xiàng)卡位于后臺(tái)時(shí)無法正確計(jì)時(shí)或同步音樂的許多問題。

限制跟蹤超時(shí)腳本

自 Firefox 55 以來,跟蹤腳本(例如 Google Analytics,F(xiàn)irefox 通過其 TP 列表識(shí)別為跟蹤腳本的任何腳本 URL )都受到進(jìn)一步限制。在前臺(tái)運(yùn)行時(shí),節(jié)流最小延遲仍為 4ms。但是,在后臺(tái)選項(xiàng)卡中,限制最小延遲時(shí)間為10000毫秒或10秒,這將在文檔第一次加載后30秒生效。

控制這種行為的前提是:

  • dom.min_tracking_timeout_value:4
  • dom.min_tracking_background_timeout_value:10000
  • dom.timeout.tracking_throttling_delay:30000

延遲超時(shí)

除了“clamping”之外,當(dāng)頁面(或操作系統(tǒng)/瀏覽器本身)忙于其他任務(wù)時(shí),超時(shí)還可以稍后觸發(fā)。需要注意的一個(gè)重要情況是,直到調(diào)用 setTimeout() 的線程已終止才能執(zhí)行該函數(shù)或代碼片段。例如:

function foo() {
  console.log('foo has been called');
}
setTimeout(foo, 0);
console.log('After setTimeout');

將寫入控制臺(tái):

After setTimeout
foo has been called

這是因?yàn)榧词?setTimeout 被稱為延遲零,它被放置在一個(gè)隊(duì)列中,并計(jì)劃在下一個(gè)機(jī)會(huì)運(yùn)行;不是馬上當(dāng)前執(zhí)行的代碼必須在執(zhí)行隊(duì)列上的函數(shù)之前完成,因此結(jié)果的執(zhí)行順序可能不如預(yù)期。

最大延遲值

包括 Internet Explorer,Chrome,Safari 和 Firefox 在內(nèi)的瀏覽器將延遲存儲(chǔ)為內(nèi)部 32 位有符號(hào)整數(shù)。當(dāng)使用大于 2147483647 的延遲(大約24.8天)時(shí),會(huì)導(dǎo)致整數(shù)溢出,導(dǎo)致立即執(zhí)行超時(shí)。

規(guī)范

規(guī)范 狀態(tài) 評(píng)論
HTML Living Standard 
在該規(guī)范中定義'WindowOrWorkerGlobalScope.setTimeout()'。
Living Standard
方法轉(zhuǎn)移到最新規(guī)范中的WindowOrWorkerGlobalScope mixin。
HTML Living Standard 
規(guī)范 中'WindowTimers.setTimeout()'的定義。             
Living Standard
初始定義(DOM Level 0)

瀏覽器兼容性

我們正在將兼容性數(shù)據(jù)轉(zhuǎn)換為機(jī)器可讀的JSON格式。

  • 電腦端
特征 Chrome
Edge
Firefox(Gecko)
Internet Explorer
Opera
Safari
基本支持 支持:1.0 支持 支持:1.0(1.7或更早)、52[2] 支持:4 支持:4 支持:1.0
支持回調(diào)參數(shù)[1] 支持 支持 支持 支持:10.0 支持 支持
限制跟蹤超時(shí)腳本 ? 支持:55 ? ?
  • 移動(dòng)端

特征AndroidChrome for AndroidEdgeFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
基本支持支持:1.0支持:1.0支持支持:1.0、52.0[2]支持:6.0支持:6.0支持:1.0
支持回調(diào)函數(shù)[1]???????
限制跟蹤超時(shí)腳本???支持:55.0 ???

注釋:

[1]它是否支持第一種形式的可選參數(shù)。

[2] setTimeout() 現(xiàn)在在 WindowOrWorkerGlobalScope mixin 上定義。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)