JavaScript метод WindowOrWorkerGlobalScope.setTimeout()
JavaScript WindowOrWorkerGlobalScopeОпределение и применение
JavaScript метод setTimeout() объекта WindowOrWorkerGlobalScope задает таймер, который выполняет функцию или указанный фрагмент кода по истечению срока заданного тайм-аута.
Обращаю Ваше внимание на то, что функция, вызванная методом setTimeout() выполняется только один раз, если Вам необходимо повторять выполнение, то используйте метод setInterval().
Метод setTimeout() возвращает положительное целое числовое значение, которое определяет таймер, это значение может быть передано методу clearTimeout(), чтобы отменить выполнение программного кода, ранее отложенного вызовом метода setTimeout().
Гарантируется, что идентификатор тайм-аута никогда не будет повторно использован последующим вызовом setTimeout(), или методом setInterval() для того же объекта (окна или рабочего объекта), однако, разные объекты используют отдельные пулы идентификаторов.
Пул идентификаторов, используемых методами setTimeout() и setInterval() являются общими, что означает, что вы можете технически использовать методы clearTimeout() и clearInterval() взаимозаменяемо. Однако для ясности вам следует избегать этого.
Поддержка браузерами
Метод | Chrome | Firefox | Opera | Safari | IExplorer | Edge |
---|---|---|---|---|---|---|
setTimeout() | Да | Да | Да | Да | Да | Да |
JavaScript синтаксис:
const timeoutID = scope.setTimeout(function, delay); const timeoutID = scope.setTimeout(function, delay, param1, ..., paramX); const timeoutID = scope.setTimeout(code, delay); // не рекомендуемый синтаксис function - Function delay - Integer param1, ..., paramX - Any code - String
Cпецификация
Document Object Model (DOM) Level 0Значения параметров
Параметр | Описание |
---|---|
function | Функция, которая будет выполняться после истечения таймера. |
delay | Время в миллисекундах, которое таймер должен ждать перед выполнением указанной функции или кода. Если этот параметр опущен, то используется значение 0, оно означает, что выполнение должно произойти немедленно, или, точнее, как можно скорее (как только завершат работу все обработчики событий). Если этот параметр меньше 4, то используется значение 4. Обратите внимание, что в любом случае фактическая задержка может быть больше, чем предполагалось, причины задержек перечислены ниже в примерах. |
param1, ..., paramX | Дополнительные параметры, передаваемые функции, указанной функцией или кодом, по истечении таймера. Передача дополнительных параметров функции в первом синтаксисе не работает в Internet Explorer 9 и ниже. Если вы хотите включить эту функцию в этом браузере, необходимо использовать полифилл. |
code | Альтернативный синтаксис, который позволяет включать строку вместо функции, которая компилируется и выполняется по истечении таймера. Этот синтаксис не рекомендован к использованию в связи с угрозой безопасности. |
Пример использования
Базовое использование
<!DOCTYPE html> <html> <head> <title>Использование JavaScript методов setTimeout() и clearTimeout() объекта WindowOrWorkerGlobalScope</title> </head> <body> <button onclick = "delayedInfo()">Show delayed info</button> <!-- добавляем атрибут событий onclick --> <button onclick = "clearMyTimeOut()">Clear timeout</button> <!-- добавляем атрибут событий onclick --> <script> let timeoutID; // создаем пустую переменную function delayedInfo() { timeoutID = setTimeout(function(){ console.log("Текстовое сообщение через 2 секунды"); }, 2000); // задаем таймер, который выполняет анонимную функцию по истечению 2000 миллисекунд (2 секунды) } function clearMyTimeOut() { console.log("Таймер с id " + timeoutID + " отменен"); // выводим в консоль значение переменной и информацию об отмене таймера clearTimeout(timeoutID); // отменяет выполнение программного кода, ранее отложенного вызовом метода setTimeout } </script> </body> </html>
В этом примере с использованием атрибута событий onclick при нажатии на первую кнопку (HTML элемент <button>) вызываем функцию delayedInfo(), которая с использованием JavaScript метода setTimeout() задает таймер, который выполняет анонимную функцию по истечению 2000 миллисекунд (2 секунды).
С использованием атрибута событий onclick при нажатии на вторую кнопку (HTML элемент <button>) вызываем функцию clearMyTimeOut(), которая с использованием JavaScript метода clearTimeout() отменяет выполнение программного кода, ранее отложенного вызовом метода setTimeout(). Попробуйте после нажатия на первую кнопку сразу нажать на вторую (в течении 2 секунд), как вы можете убедиться в этом случае вызов отложенной функции не произойдет.
Результат нашего примера:
Проблема с this
Когда вы передаете методу setTimeout() (или любой другой функции) метод, то он будет вызываться со значением this, которое может отличаться от вашего ожидания.
Код, выполняемый setTimeout(), вызывается из контекста выполнения, отдельного от функции, из которой был вызван setTimeout(). Обычные правила установки контекста для ключевого слова this для вызываемой функции применяются, и если вы не установили его явно в вызове или с определенной привязкой, то он по умолчанию будет глобальным (или оконным) объектом в нестрогом режиме или будет неопределенным в строгом режиме. Он не будет таким же, как this для функции, которая вызвала метод setTimeout(). Значение this по умолчанию метода setTimeout() по-прежнему будет объектом window, а не неопределенным, даже в строгом режиме. Давайте рассмотрим следующий пример:
const myObj = { arr: ["a", "b", "c"], method: function(x) { console.log("Элемент " + this.arr[x]);} // выводим в консоль определенный элемент массива } myObj.method(1); Элемент b myObj.method(2); Элемент c setTimeout(myObj.method, 1000, 1); // Cannot read property '1' of undefined
Обратите внимание, что при передаче метода методу setTimeout() мы получаем ошибку. Оптимальным и самым простым решением этой проблемы в данном случае будет использование анонимной функции обертки:
setTimeout(function(){ myObj.method(2) }, 1000); // с передачей дополнительных параметров вызываемому методу внутри анонимной функции Элемент c setTimeout(function(x){ myObj.method(x) }, 1000, 2); // с передачей дополнительных параметров анонимной функции и вызываемому методу Элемент c // с использованием стрелочных функций setTimeout(() => { myObj.method(1) }, 1000); // с передачей дополнительных параметров вызываемому методу внутри анонимной функции Элемент b setTimeout((x) => { myObj.method(x) }, 1000, 1); // с передачей дополнительных параметров анонимной функции и вызываемому методу Элемент b
Передача строковых литералов
Метод setTimeout() имеет альтернативный синтаксис, который позволяет включать строку вместо функции, которая компилируется и выполняется по истечении таймера:
// не рекомендованный синтаксис setTimeout("console.log('hello');", 1000); // рекомендованный синтаксис setTimeout(myFunc, 1000); setTimeout(function() { console.log("hello"); }, 1000); setTimeout(() => { console.log("hello"); }, 1000); // допускается использование стрелочных функций
Обратите внимание, что альтернативный синтаксис не рекомендован к использованию в связи с угрозой безопасности.
Максимальная задержка выполнения
Браузеры, включая Internet Explorer, Chrome, Safari и Firefox хранят значение тайм-аута как 32-разрядное целое число со знаком внутри. Значения выше 2147483647 миллисекунд (около 24,8 дней) вызовут переполнение, в результате чего вызов переданной функции произойдет немедленно.
Причины увеличения задержек
Давайте с Вами рассмотрим распространенные причины по которым тайм-аут может занять больше времени, чем ожидалось.
В современных браузерах вызовы методов setTimeout() или setInterval() могут быть произведены как минимум один раз в 4 миллисекунды, когда последовательные вызовы инициируются из-за вложенности обратного вызова, или после определенного количества последовательных интервалов.
Чтобы уменьшить нагрузку (и связанное с ней использование батареи) с фоновых вкладок, тайм-ауты регулируются до срабатывания не чаще одного раза в секунду (1000 миллисекунд) в неактивных вкладках.
На увеличение задержки также может повлить, когда страница (или сама ОС/браузер) занята другими задачами. Важно отметить, что функция или фрагмент кода не могут быть выполнены до тех пор, пока поток, вызвавший setTimeout() не завершится, например:
function f() { console.log("f has been called"); } setTimeout(f, 0); console.log("after setTimeout"); // вывод в консоль будет следующий: after setTimeout f has been called
В этом примере не смотря на то, что setTimeout() был вызван с нулевой задержкой, он был помещен в очередь и запланирован для запуска при следующей возможности, а не сразу. Текущий код должен быть завершен до выполнения функций в очереди, по этой причине порядок выполнения может быть не таким, как ожидалось.
Причины увеличения задержек
Давайте с Вами рассмотрим распространенные причины по которым тайм-аут может занять больше времени, чем ожидалось.
В современных браузерах вызовы методов setTimeout() или setInterval() могут быть произведены как минимум один раз в 4 миллисекунды, когда последовательные вызовы инициируются из-за вложенности обратного вызова, или после определенного количества последовательных интервалов.
Чтобы уменьшить нагрузку (и связанное с ней использование батареи) с фоновых вкладок, тайм-ауты регулируются до срабатывания не чаще одного раза в секунду (1000 миллисекунд) в неактивных вкладках.
На увеличение задержки также может повлить, когда страница (или сама ОС/браузер) занята другими задачами. Важно отметить, что функция или фрагмент кода не могут быть выполнены до тех пор, пока поток, вызвавший setInterval() не завершится, например:
function f() { console.log("f has been called"); } setInterval(f, 0); console.log("after setInterval"); // вывод в консоль будет следующий: after setInterval f has been called
В этом примере не смотря на то, что setInterval() был вызван с нулевой задержкой, он был помещен в очередь и запланирован для запуска при следующей возможности, а не сразу. Текущий код должен быть завершен до выполнения функций в очереди, по этой причине порядок выполнения может быть не таким, как ожидалось.
Длительность выполнения меньше интервала
Если существует вероятность того, что выполнение логики может занять больше времени, чем интервал времени, то рекомендуется вместо использования метода setInterval() рекурсивно вызвать именованную функцию с помощью метода setTimeout(). Например, при использовании setInterval() для опроса удаленного сервера каждые 5 секунд задержка в сети, не отвечающий сервер и множество других проблем могут помешать выполнению запроса в отведенное время. Таким образом, вы можете оказаться в очереди с запросами XHR, которые не обязательно вернутся в необходимом порядке. В этих случаях предпочтителен рекурсивный вызов метода setTimeout():
(function loop(){ setTimeout(function() { // ваш код loop(); }, 2000); // необходимая задержка })();
В приведенном выше фрагменте объявлена именованная функция loop(), которая немедленно выполняется, эта функция рекурсивно вызывается внутри метода setTimeout() после завершения выполнения логики. Хотя этот шаблон и не гарантирует выполнение на фиксированном интервале, но он гарантирует, что предыдущий интервал был завершен до рекурсии.
JavaScript WindowOrWorkerGlobalScope