Spec-Zone.ru › Web APIs

Метод Window: setTimeout()

Базовая Широко доступная

Эта функция хорошо зарекомендовала себя и работает на многих устройствах и версиях браузеров. Она доступна во всех браузерах с июля 2015 года.

  • Узнать больше
  • Полная совместимость
  • Отправить отзыв

Метод setTimeout() интерфейса Window устанавливает таймер, который выполняет функцию или указанный фрагмент кода, когда таймер истекает.

Синтаксис

setTimeout(code)
setTimeout(code, delay)

setTimeout(functionRef)
setTimeout(functionRef, delay)
setTimeout(functionRef, delay, param1)
setTimeout(functionRef, delay, param1, param2)
setTimeout(functionRef, delay, param1, param2, /* …, */ paramN)

Параметры

functionRef

Функция function, которая должна быть выполнена после истечения таймера.

code

Альтернативный синтаксис, позволяющий указать строку вместо функции. Эта строка компилируется и выполняется по истечении таймера. Этот синтаксис не рекомендуется по тем же причинам, по которым использование eval() представляет собой угрозу безопасности.

delay Необязательно

Время в миллисекундах, которое таймер должен ожидать перед выполнением указанной функции или кода. Если этот параметр опущен, используется значение 0, что означает выполнение «немедленно» или, точнее, в следующем цикле событий.

Обратите внимание, что фактическое время ожидания может быть больше, чем указано; см. Причины задержек, превышающих заданные ниже.

Также обратите внимание, что если значение не является числом, неявное преобразование типов применяется к значению для преобразования его в число — это может привести к непредсказуемым и неожиданным результатам; см. Значения задержки, не являющиеся числами, неявно преобразуются в числа для примера.

param1, …, paramN Необязательно

Дополнительные аргументы, которые передаются функции, указанной параметром functionRef.

Возвращаемое значение

Возвращаемое timeoutID значение — положительное целое число, которое идентифицирует таймер, созданный вызовом setTimeout(). Это значение может быть передано в clearTimeout() для отмены таймаута.

Гарантируется, что значение timeoutID никогда не будет повторно использовано последующим вызовом setTimeout() или setInterval() в том же окне, пока таймер не завершит работу. Однако разные объекты используют отдельные пулы идентификаторов.

Описание

Таймауты отменяются с помощью Window.clearTimeout().

Чтобы вызывать функцию многократно (например, каждую N миллисекунд), используйте setInterval().

Значения задержки, не являющиеся числами, неявно преобразуются в числа

Если setTimeout() вызывается со значением delay, которое не является числом, неявное преобразование типов молча выполняется для преобразования значения в число. Например, следующий код неправильно использует строку "1000" для значения delay вместо числа 1000 — но тем не менее работает, потому что при выполнении кода строка преобразуется в число 1000, и поэтому код выполняется через 1 секунду.

setTimeout(() => {
  console.log("Delayed for 1 second.");
}, "1000");

Однако во многих случаях неявное преобразование типов может привести к непредсказуемым и неожиданным результатам. Например, когда выполняется следующий код, строка "1 second" в конечном итоге преобразуется в число 0 — и поэтому код выполняется немедленно, без задержки.

setTimeout(() => {
  console.log("Delayed for 1 second.");
}, "1 second");

Поэтому не используйте строки для значения delay, а всегда используйте числа:

setTimeout(() => {
  console.log("Delayed for 1 second.");
}, 1000);

Работа с асинхронными функциями

setTimeout() — асинхронная функция, что означает, что функция таймера не приостановит выполнение других функций в стеке функций. Другими словами, вы не можете использовать setTimeout() для создания «паузы» перед запуском следующей функции в стеке.

Рассмотрим следующий пример:

setTimeout(() => {
  console.log("this is the first message");
}, 5000);
setTimeout(() => {
  console.log("this is the second message");
}, 3000);
setTimeout(() => {
  console.log("this is the third message");
}, 1000);

// Output:

// this is the third message
// this is the second message
// this is the first message

Обратите внимание, что первая функция не создает 5-секундной «паузы» перед вызовом второй функции. Вместо этого первая функция вызывается, но ожидает 5 секунд для выполнения. Пока первая функция ждет выполнения, вторая функция вызывается, и к ней применяется 3-секундная задержка перед выполнением. Так как таймеры ни одной, ни другой функции не завершены, третья функция вызывается и завершает свою работу первой. Затем выполняется вторая. И наконец, первая функция выполняется после завершения таймера.

Чтобы создать последовательность, в которой одна функция выполняется только после завершения другой, ознакомьтесь с документацией по Promises.

Проблема «this»

При передаче метода в setTimeout(), он будет вызван со значением this, которое может отличаться от вашего ожидания. Общая проблема подробно описана в справочнике по JavaScript.

Код, выполняемый setTimeout(), вызывается из контекста выполнения, отличного от контекста функции, из которой был вызван setTimeout. Применяются обычные правила для установки ключевого слова this для вызываемой функции, и если вы не установили this в вызове или с помощью bind, оно будет по умолчанию установлено на объект window (или global ), даже в строгом режиме. Оно не будет таким же, как значение this для функции, которая вызвала setTimeout.

Рассмотрим следующий пример:

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

myArray.myMethod(); // prints "zero,one,two"
myArray.myMethod(1); // prints "one"

Вышеприведенный код работает, потому что когда myMethod вызывается, его this устанавливается на myArray вызовом, поэтому внутри функции this[sProperty] эквивалентно myArray[sProperty]. Однако в следующем:

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

Функция myArray.myMethod передается setTimeout, а затем, когда она вызывается, ее this не устанавливается, поэтому по умолчанию используется объект window.

Также нет возможности передать thisArg в setTimeout, как это есть в методах массива, таких как forEach() и reduce(). Как показано ниже, использование call для установки this также не работает.

setTimeout.call(myArray, myArray.myMethod, 2.0 * 1000); // error
setTimeout.call(myArray, myArray.myMethod, 2.5 * 1000, 2); // same error

Решение

Использование обертывающей функции

Распространённый способ решения проблемы — использование обертывающей функции, которая устанавливает this на необходимое значение:

setTimeout(function () {
  myArray.myMethod();
}, 2.0 * 1000); // prints "zero,one,two" after 2 seconds
setTimeout(function () {
  myArray.myMethod("1");
}, 2.5 * 1000); // prints "one" after 2.5 seconds

Обертывающая функция может быть стрелочной функцией:

setTimeout(() => {
  myArray.myMethod();
}, 2.0 * 1000); // prints "zero,one,two" after 2 seconds
setTimeout(() => {
  myArray.myMethod("1");
}, 2.5 * 1000); // prints "one" after 2.5 seconds
Использование bind()

В качестве альтернативы можно использовать bind() для установки значения this для всех вызовов данной функции:

const myArray = ["zero", "one", "two"];
const 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, 1.0 * 1000); // still prints "zero,one,two" after 1 second because of the binding
setTimeout(myBoundMethod, 1.5 * 1000, "1"); // prints "one" after 1.5 seconds

Передача строковых литералов

Передача строки вместо функции в setTimeout() имеет те же проблемы, что и использование eval().

// Don't do this
setTimeout("console.log('Hello World!');", 500);
// Do this instead
setTimeout(() => {
  console.log("Hello World!");
}, 500);

Строка, переданная в setTimeout(), оценивается в глобальном контексте, поэтому локальные символы в контексте, где был вызван setTimeout(), недоступны при оценке строки как кода.

Причины задержек, превышающих заданные

Существует несколько причин, по которым таймаут может срабатывать дольше, чем ожидалось. В этом разделе описываются наиболее распространённые причины.

Вложенные таймауты

Как указано в стандарте HTML, браузеры будут применять минимальный таймаут в 4 миллисекунды после того, как вложенный вызов setTimeout был запланирован 5 раз.

Это можно увидеть в следующем примере, в котором мы вкладываем вызов setTimeout с задержкой в 0 миллисекунды и регистрируем задержку каждый раз, когда вызывается обработчик. В первые четыре раза задержка составляет примерно 0 миллисекунд, а после этого — примерно 4 миллисекунды:

<button id="run">Run</button>
<table>
  <thead>
    <tr>
      <th>Previous</th>
      <th>This</th>
      <th>Actual delay</th>
    </tr>
  </thead>
  <tbody id="log"></tbody>
</table>
let last = 0;
let iterations = 10;

function timeout() {
  // log the time of this call
  log(new Date().getMilliseconds());
  // if we are not finished, schedule the next call
  if (iterations-- > 0) {
    setTimeout(timeout, 0);
  }
}

function run() {
  // clear the log
  const log = document.querySelector("#log");
  while (log.lastElementChild) {
    log.removeChild(log.lastElementChild);
  }

  // initialize iteration count and the starting timestamp
  iterations = 10;
  last = new Date().getMilliseconds();
  // start timer
  setTimeout(timeout, 0);
}

function log(now) {
  // log the last timestamp, the new timestamp, and the difference
  const tableBody = document.getElementById("log");
  const logRow = tableBody.insertRow();
  logRow.insertCell().textContent = last;
  logRow.insertCell().textContent = now;
  logRow.insertCell().textContent = now - last;
  last = now;
}

document.querySelector("#run").addEventListener("click", run);

Таймауты в неактивных вкладках

Для уменьшения нагрузки (и связанного с ней расхода заряда батареи) от фоновых вкладок браузеры будут применять минимальную задержку таймаута в неактивных вкладках. Она также может быть отменена, если на странице воспроизводится звук с использованием API Web Audio AudioContext.

Подробности зависят от браузера:

  • В Firefox Desktop и Chrome минимальный таймаут для неактивных вкладок составляет 1 секунду.
  • В Firefox для Android минимальный таймаут для неактивных вкладок составляет 15 минут, и они могут быть полностью закрыты.
  • Firefox не ограничивает неактивные вкладки, если вкладка содержит AudioContext.

Ограничение отслеживающих скриптов

Firefox применяет дополнительное ограничение для скриптов, которые распознаются как отслеживающие скрипты. При работе в фоновом режиме минимальная задержка ограничения по-прежнему составляет 4 мс. Однако в неактивных вкладках минимальная задержка ограничения составляет 10 000 мс или 10 секунд, что вступает в силу через 30 секунд после первой загрузки документа.

Дополнительную информацию см. в разделе Защита от отслеживания.

Поздние таймауты

Таймаут также может сработать позже, чем ожидалось, если страница (или операционная система/браузер) занята другими задачами. Важно отметить, что функция или фрагмент кода не могут быть выполнены до завершения потока, который вызвал setTimeout(). Например:

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

Выведет в консоль:

After setTimeout
foo has been called

Это происходит потому, что даже если setTimeout был вызван с задержкой ноль, он помещается в очередь и запланирован на выполнение в следующий момент; не сразу. Текущий код должен быть завершен, прежде чем функции в очереди будут выполнены, поэтому порядок выполнения может отличаться от ожидаемого.

Отсрочка таймаутов во время загрузки страницы

Firefox отложит срабатывание таймеров setTimeout() пока текущая вкладка загружается. Вызов откладывается до тех пор, пока основной поток не будет считаться неактивным (аналогично Window.requestIdleCallback()) или до срабатывания события загрузки.

Веб-расширения: фоновые страницы и таймеры

В WebExtensions, setTimeout() не работает надёжно. Разработчики расширений должны использовать API alarms вместо него.

Максимальное значение задержки

Браузеры хранят задержку в виде 32-битного знакового целого числа внутри. Это приводит к переполнению целых чисел при использовании задержек больше 2 147 483 647 мс (приблизительно 24,8 дня). Например, этот код:

setTimeout(() => console.log("hi!"), 2 ** 32 - 5000);

…приводит к немедленному выполнению таймаута (поскольку 2**32 - 5000 переполняется до отрицательного числа), в то время как следующий код:

setTimeout(() => console.log("hi!"), 2 ** 32 + 5000);

…приводит к выполнению таймаута приблизительно через 5 секунд.

Примечание: Это отличается от поведения setTimeout в Node.js, где любой таймаут больше 2 147 483 647 мс приводит к немедленному выполнению.

Примеры

Установка и удаление таймаутов

В следующем примере на веб-странице создаются две простые кнопки и к ним привязываются функции setTimeout() и clearTimeout(). Нажатие первой кнопки установит таймаут, который отобразит сообщение через две секунды, а идентификатор таймаута будет сохранён для использования clearTimeout(). Вы можете отменить этот таймаут, нажав вторую кнопку.

HTML

<button onclick="delayedMessage();">Show a message after two seconds</button>
<button onclick="clearMessage();">Cancel message before it happens</button>

<div id="output"></div>

JavaScript

let timeoutID;

function setOutput(outputContent) {
  document.querySelector("#output").textContent = outputContent;
}

function delayedMessage() {
  setOutput("");
  timeoutID = setTimeout(setOutput, 2 * 1000, "That was really slow!");
}

function clearMessage() {
  clearTimeout(timeoutID);
}

Результат

См. также пример clearTimeout().

Спецификации

Спецификация
HTML
# dom-settimeout-dev

Совместимость с браузерами

Рабочие столы Мобильные
Chrome Edge Firefox Opera Safari Chrome Android Firefox для Android Opera Android Safari на iOS Samsung Internet WebView Android
setTimeout 1 12 1 4 1 18 4 10.1 1 1.0 4.4
supports_parameters_for_callback 1 12 1 ≤15 1.2 18 4 ≤14 1 1.0 ≤37
worker_support 3 12 3.5 ≤12.1 4 18 4 ≤12.1 5 1.0 4.4

См. также

  • Полифил setTimeout, который позволяет передавать аргументы в обратный вызов в core-js
  • Window.clearTimeout()
  • WorkerGlobalScope.setTimeout()
  • Window.setInterval()
  • Window.requestAnimationFrame()
  • Window.queueMicrotask()

© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API