WindowTimers.setTimeout()
setTimeout()
, що належить об'єкту WindowOrWorkerGlobalScope
, та наслідується від window.setTimeout, встановлює таймер, що виконує функцію або вказаний фрагмент коду один раз, щойно спливе заданий час.Синтаксис
var timeoutID = window.setTimeout(func[, delay, param1, param2, ...]); var timeoutID = window.setTimeout(code[, delay]);
Параметри
func
function
котру необхідно виконати після того, як вийде заданий час.code
- Опційний синтаксис, котрий дозволяє задавати рядок замість функції, котра компілюється і виконується при спрацюванні таймера. Використання цього синтаксису не рекомендовано з тих же ж причин, котрі роблять застосування
eval()
загрозою безпеці. delay
Optional- Час у мілісекундах (тисячних секунди), котрий таймер має зачекати перед тим, як виконати вказану функцію або код. Якщо цей параметр пропустити, буде використано значення 0. Варто зазначити, що справжній час затримки, може бути довшим. Дивіться розділ Причини, з яких затримка є фактично довшою ніж задано
param1, ..., paramN
Optional- Додаткові параметри, що передаються до func або code, щойно час таймера спливе.
Зверніть увагу: Internet Explorer 9 та більш ранні версії не підтримують додаткові параметри. Застосовуйте поліфіл, щобл уможливити цей функціонал (див. розділ Callback arguments).
Значення, що повертається
Після виклику setTimeout()
, повертається timeoutID
- це не нульове цифрове значення, що використовується для ідентифікації створеного таймера. Цей ідентифікатор можна передати як параметр до Window.clearTimeout()
(en-US) щоб відмінити таймер.
Корисно знати, що setTimeout()
та setInterval()
(en-US) використовують спільну колекцію ID ідентифікаторів, а також що clearTimeout()
та clearInterval()
(en-US) технічно можуть бути взаємозамінними. Тим не менше, задля ясності, варто завжди зіставляти їх, аби не допускати помилок під час розробки коду.
Приклад
У наступному прикладі створено дві кнопки на веб сторінці, що прив'язані до setTimeout()
та clearTimeout()
процесів. Після натискання на першу кнопку встановлюється таймер, що викликає діалогове вікно alert через дві секунди та зберігає ID таймера для подальшого використання у clearTimeout()
. За бажанням ви можете відмінити цей таймер, якщо натисните другу кнопку.
HTML код
<p>Live Example</p>
<button onclick="delayedAlert();">Відобразити діалогове вікно alert через дві секунди.</button>
<p></p>
<button onclick="clearAlert();">Відмінити відображення діалогового вікна.</button>
JavaScript код
var timeoutID;
function delayedAlert() {
timeoutID = window.setTimeout(slowAlert, 2000);
}
function slowAlert() {
alert("Це було дійсно повільно!");
}
function clearAlert() {
window.clearTimeout(timeoutID);
}
Результат
Дивіться також clearTimeout()
example.
Поліфіл
Якщо вам необхідно передати один чи декілька аргументів до колбек функції, а також потрібно, щоб цей функціонал працював у браузерах, що не підтримують передачу додаткових аргументів для setTimeout()
чи setInterval()
(наприклад, версії Internet Explorer 9 чи нижче), ви можете застосувати цей поліфіл, який активує стандартну HTML5 функціональність. Просто додайте цей код на початку вашого скрипта:
/*\
|*|
|*| 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
Якщо вам потрібен фікс, який не впливатиме на роботу жодного іншого мобільного чи десктопного браузера, окрім IE9 та нижче, ви можете скористатись умовними коментарями 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
@*/
Або використати умовні коментарі HTML для IE9 та нижче:
<!--[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]-->
Тимчасові рішення
Ще одне можливе рішення - використання анонімної функції для виклику колбека, але це вартісне рішення. Приклад:
var intervalID = setTimeout(function() { myFunc("one", "two", "three"); }, 1000);
Приклад, наведений вище, може бути також написаний за допомогою arrow function:
var intervalID = setTimeout(() => { myFunc("one", "two", "three"); }, 1000);
Ще одне рішення - використання function's bind
. Приклад:
setTimeout(function(arg1){}.bind(undefined, 10), 1000);
Проблема "this
"
Коли ви передаєте до setTimeout()
метод або будь-яку іншу функцію, ймовірно вона буде викликана не з тим значенням this
, на яке ви очікуєте. Ця проблема детально описана у JavaScript reference.
Пояснення
Код, що виконується всередині setTimeout()
, викликається із іншого контекста виконання (execution context), ніж у функції, яка викликала setTimeout
. До функції, яку викликають всередині setTimeout
застосовуються звичайні правила призначення this
. І, якщо ви не встановили this
під час виклику або за допомогою bind
, його значенням за замовчуванням буде об'єкт global
(або window
) у нестрогому режимі, або undefined
у строгому режимі. Значення this
буде іншим, аніж у функції, яка викликала setTimeout
. Розгляньте наступний приклад:
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"
Приклад вище працює тому, що myMethod
викликано, як метод масиву myArray
. Тому його this
дорівнює myArray
, а значення this[sProperty]
всередині метода дорівнює myArray[sProperty]
. Тим не менше, у наступному прикладі:
setTimeout(myArray.myMethod, 1000); // prints "[object Window]" after 1 second
setTimeout(myArray.myMethod, 1500, "1"); // prints "undefined" after 1.5 seconds
Метод myArray.myMethod
передано до setTimeout
, як звичайну функцію, якій не задано значення this
. І коли вона викликається, її this
за замовчуванням дорівнює об'єкту window
. У setTimeout
неможливо передати this
аргументом, як, наприклад, у методи Array (forEach, reduce, тощо), або через використання call
, як показано у прикладі нижче:
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
Можливі рішення
Типовий спосіб вирішення цієї проблеми - використання функції обгортки, яка встановлює 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
Стрілочна функція також є прийнятною альтернативою:
setTimeout(() => {myArray.myMethod()}, 2000); // prints "zero,one,two" after 2 seconds
setTimeout(() => {myArray.myMethod('1')}, 2500); // prints "one" after 2.5 seconds
Ще одне можливе рішення проблеми this
- замінити нативні глобальні функції setTimeout()
та setInterval()
кастомними функціями, які можуть приймати об'єкт this
і застосовувати його у колбек функції, використовуючи Function.prototype.call
. Наприклад:
// 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);
};
Випробування нового підхода:
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
Function.prototype.bind()
, який встановлює значення this
для кожного виклику вказаної функції. Це допомагає уникнути використання функції обгортки для призначення 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
Примітки
Таймер можна скасувати за допомогою Window.clearTimeout()
(en-US). Щоб викликати функцію повторно (наприклад, через кожні N мілісекунди), використовуйте Window.setInterval()
(en-US).
Важливо пам'ятати, що функція або фрагмент коду не можуть бути виконані, допоки не буде завершено поток функції, яка викликала setTimeout()
. Наприклад.
function foo(){
console.log('foo has been called');
}
setTimeout(foo, 0);
console.log('After setTimeout');
Виведе в консоль:
After setTimeout
foo has been called
Тому що, навіть не зважаючи на те, що setTimeout
було викликано із нульовою затримкою, вона переміщується у чергу і її виконання відбудеться у найближчому наступному циклі, тобто не відразу. Код, що в данний момент виконується, повинен бути завершений. Тільки після цього функції, переміщені в чергу, будуть виконані. Тому порядок виконання може бути іншим, аніж очікувалось.
Passing string literals
Passing a string instead of a function to setTimeout()
suffers from the same hazards as using eval.
// Recommended
window.setTimeout(function() {
alert("Hello World!");
}, 500);
// Not recommended
window.setTimeout("alert('Hello World!');", 500);
A string passed to setTimeout
is evaluated in the global context, so local symbols in the context where setTimeout()
was called will not be available when the string is evaluated as code.
Причини чому затримка є фактично довшою ніж вказано
There are a number of reasons why a timeout may take longer to fire than anticipated. This section describes the most common reasons.
Nested timeouts forced to >=4ms
Historically browsers implement setTimeout()
"clamping": successive setTimeout()
calls with delay
smaller than the "minimum delay" limit are forced to use at least the minimum delay. The minimum delay, DOM_MIN_TIMEOUT_VALUE
, is 4 ms (stored in a preference in Firefox: dom.min_timeout_value
), with a DOM_CLAMP_TIMEOUT_NESTING_LEVEL
of 5.
In fact, 4 ms is specified by the HTML5 spec and is consistent across browsers released in 2010 and onward. Prior to (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2), the minimum timeout value for nested timeouts was 10 ms.
To implement a 0 ms timeout in a modern browser, you can use window.postMessage()
(en-US) as described here.
Timeouts in inactive tabs clamped to >=1000ms
To reduce the load (and associated battery usage) from background tabs, timeouts are often clamped to firing no more often than once per second (1000 ms) in inactive tabs.
Firefox implements this behavior since version 5 (see bug 633421, the 1000ms constant can be tweaked through the dom.min_background_timeout_value
preference). Chrome implements this behavior since version 11 (crbug.com/66078).
Firefox for Android uses a timeout value of 15 minutes for background tabs since bug 736602 in Firefox 14, and background tabs can also be unloaded entirely.
Firefox 50 no longer throttles background tabs if a Web Audio API AudioContext
(en-US) is actively playing sound. Firefox 51 further amends this such that background tabs are no longer throttled if an AudioContext
(en-US) is present in the tab at all, even if no sound is being played. These resolve a number of issues with apps which play note-based music not being able to time or synchronize the music properly when the tab is in the background.
Late timeouts
In addition to "clamping", the timeout can also fire later when the page (or the OS/browser itself) is busy with other tasks
Maximum delay value
Browsers including Internet Explorer, Chrome, Safari, and Firefox store the delay as a 32-bit signed integer internally. This causes an integer overflow when using delays larger than 2147483647, resulting in the timeout being executed immediately.
Specifications
Specification | Status | Comment |
---|---|---|
HTML Living Standard The definition of 'WindowTimers.setTimeout()' in that specification. |
Living Standard | Initial definition (DOM Level 0) |
Browser compatibility
Feature | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
Basic support | 1.0 | 1.0 (1.7 or earlier) | 4.0 | 4.0 | 1.0 |
Supports parameters for callback[1] | (Yes) | (Yes) | 10.0 | (Yes) | (Yes) |
Feature | Android | Chrome for Android | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|
Basic support | 1.0 | 1.0 | 1.0 (1) | 6.0 | 6.0 | 1.0 |
Supports parameters for callback[1] | ? | ? | ? | ? | ? | ? |
[1] Whether it supports the optional parameters when in its first form or not.