Scheduling: setTimeout and setInterval
We may decide to execute a function not right now, but at a certain time later. That’s called “scheduling a call”.
There are two methods for it:
- setTimeout allows us to run a function once after the interval of time.
- setInterval allows us to run a function repeatedly, starting after the interval of time, then repeating continuously at that interval.
These methods are not a part of JavaScript specification. But most environments have the internal scheduler and provide these methods. In particular, they are supported in all browsers and Node.js.
setTimeout
func|code Function or a string of code to execute. Usually, that’s a function. For historical reasons, a string of code can be passed, but that’s not recommended. delay The delay before run, in milliseconds (1000 ms = 1 second), by default 0. arg1 , arg2 … Arguments for the function (not supported in IE9-)
For instance, this code calls sayHi() after one second:
If the first argument is a string, then JavaScript creates a function from it.
So, this will also work:
But using strings is not recommended, use arrow functions instead of them, like this:
Novice developers sometimes make a mistake by adding brackets () after the function:
That doesn’t work, because setTimeout expects a reference to a function. And here sayHi() runs the function, and the result of its execution is passed to setTimeout . In our case the result of sayHi() is undefined (the function returns nothing), so nothing is scheduled.
Canceling with clearTimeout
A call to setTimeout returns a “timer identifier” timerId that we can use to cancel the execution.
The syntax to cancel:
In the code below, we schedule the function and then cancel it (changed our mind). As a result, nothing happens:
As we can see from alert output, in a browser the timer identifier is a number. In other environments, this can be something else. For instance, Node.js returns a timer object with additional methods.
Again, there is no universal specification for these methods, so that’s fine.
For browsers, timers are described in the timers section of HTML5 standard.
setInterval
The setInterval method has the same syntax as setTimeout :
All arguments have the same meaning. But unlike setTimeout it runs the function not only once, but regularly after the given interval of time.
To stop further calls, we should call clearInterval(timerId) .
The following example will show the message every 2 seconds. After 5 seconds, the output is stopped:
In most browsers, including Chrome and Firefox the internal timer continues “ticking” while showing alert/confirm/prompt .
So if you run the code above and don’t dismiss the alert window for some time, then in the next alert will be shown immediately as you do it. The actual interval between alerts will be shorter than 2 seconds.
Nested setTimeout
There are two ways of running something regularly.
One is setInterval . The other one is a nested setTimeout , like this:
The setTimeout above schedules the next call right at the end of the current one (*) .
The nested setTimeout is a more flexible method than setInterval . This way the next call may be scheduled differently, depending on the results of the current one.
For instance, we need to write a service that sends a request to the server every 5 seconds asking for data, but in case the server is overloaded, it should increase the interval to 10, 20, 40 seconds…
Here’s the pseudocode:
And if the functions that we’re scheduling are CPU-hungry, then we can measure the time taken by the execution and plan the next call sooner or later.
Nested setTimeout allows to set the delay between the executions more precisely than setInterval .
Let’s compare two code fragments. The first one uses setInterval :
The second one uses nested setTimeout :
For setInterval the internal scheduler will run func(i++) every 100ms:
The real delay between func calls for setInterval is less than in the code!
That’s normal, because the time taken by func ‘s execution “consumes” a part of the interval.
It is possible that func ‘s execution turns out to be longer than we expected and takes more than 100ms.
In this case the engine waits for func to complete, then checks the scheduler and if the time is up, runs it again immediately.
In the edge case, if the function always executes longer than delay ms, then the calls will happen without a pause at all.
And here is the picture for the nested setTimeout :
The nested setTimeout guarantees the fixed delay (here 100ms).
That’s because a new call is planned at the end of the previous one.
When a function is passed in setInterval/setTimeout , an internal reference is created to it and saved in the scheduler. It prevents the function from being garbage collected, even if there are no other references to it.
For setInterval the function stays in memory until clearInterval is called.
There’s a side-effect. A function references the outer lexical environment, so, while it lives, outer variables live too. They may take much more memory than the function itself. So when we don’t need the scheduled function anymore, it’s better to cancel it, even if it’s very small.
Zero delay setTimeout
There’s a special use case: setTimeout(func, 0) , or just setTimeout(func) .
This schedules the execution of func as soon as possible. But the scheduler will invoke it only after the currently executing script is complete.
So the function is scheduled to run “right after” the current script.
For instance, this outputs “Hello”, then immediately “World”:
The first line “puts the call into calendar after 0ms”. But the scheduler will only “check the calendar” after the current script is complete, so «Hello» is first, and «World» – after it.
There are also advanced browser-related use cases of zero-delay timeout, that we’ll discuss in the chapter Event loop: microtasks and macrotasks.
In the browser, there’s a limitation of how often nested timers can run. The HTML5 standard says: “after five nested timers, the interval is forced to be at least 4 milliseconds.”.
Let’s demonstrate what it means with the example below. The setTimeout call in it re-schedules itself with zero delay. Each call remembers the real time from the previous one in the times array. What do the real delays look like? Let’s see:
First timers run immediately (just as written in the spec), and then we see 9, 15, 20, 24. . The 4+ ms obligatory delay between invocations comes into play.
The similar thing happens if we use setInterval instead of setTimeout : setInterval(f) runs f few times with zero-delay, and afterwards with 4+ ms delay.
That limitation comes from ancient times and many scripts rely on it, so it exists for historical reasons.
For server-side JavaScript, that limitation does not exist, and there exist other ways to schedule an immediate asynchronous job, like setImmediate for Node.js. So this note is browser-specific.
Summary
- Methods setTimeout(func, delay, . args) and setInterval(func, delay, . args) allow us to run the func once/regularly after delay milliseconds.
- To cancel the execution, we should call clearTimeout/clearInterval with the value returned by setTimeout/setInterval .
- Nested setTimeout calls are a more flexible alternative to setInterval , allowing us to set the time between executions more precisely.
- Zero delay scheduling with setTimeout(func, 0) (the same as setTimeout(func) ) is used to schedule the call “as soon as possible, but after the current script is complete”.
- The browser limits the minimal delay for five or more nested call of setTimeout or for setInterval (after 5th call) to 4ms. That’s for historical reasons.
Please note that all scheduling methods do not guarantee the exact delay.
For example, the in-browser timer may slow down for a lot of reasons:
- The CPU is overloaded.
- The browser tab is in the background mode.
- The laptop is on battery.
All that may increase the minimal timer resolution (the minimal delay) to 300ms or even 1000ms depending on the browser and OS-level performance settings.
Tasks
Output every second
Write a function printNumbers(from, to) that outputs a number every second, starting from from and ending with to .
Make two variants of the solution.
- Using setInterval .
- Using nested setTimeout .
solution
Примеры jQuery-функции setTimeout()
JavaScript timeout представляет собой нативную javascript-функцию , которая исполняет фрагмент кода после установленной временной задержки ( в миллисекундах ). Это может пригодиться, когда нужно вывести всплывающее окошко после того, как пользователь провел некоторое время на вашей странице. Или нужно, чтобы эффект при наведении курсора на элемент запускался лишь спустя какое-то время. Таким образом, можно избежать непреднамеренного запуска эффекта, если пользователь навел курсор случайно.
Простой пример setTimeout
Чтобы продемонстрировать действие этой функции, предлагаю взглянуть на следующее демо, в котором всплывающее окно появляется спустя две секунды после клика по кнопке.
Синтаксис
В документации MDN приведен следующий синтаксис для setTimeout :
- timeoutID – числовой id , который можно использовать в сочетании с clearTimeout() для отключения таймера;
- func – функция, которая должна быть выполнена;
- code ( в альтернативном синтаксисе ) – строка кода, которую нужно исполнить;
- delay – длительность задержки в миллисекундах, после которой будет запущена функция. По умолчанию установлено значение 0.
setTimeout vs window.setTimeout
В приведенном выше синтаксисе используется window.setTimeout . Почему?
На самом деле, setTimeout и window.setTimeout – это практически одна и та же функция. Единственная разница заключается в том, что во втором выражении мы используем метод setTimeout как свойство глобального объекта window .
Лично я считаю, что это лишь сильно усложняет код. Если бы мы определили альтернативный метод JavaScript timeout , который может быть найден и возвращен в приоритетном порядке, то столкнулись бы с еще большими проблемами.
В данном руководстве я не хочу связываться с объектом window , но в целом, вы сами решаете, какой синтаксис стоит использовать.
Примеры использования
setTimeout принимает ссылки на функцию в качестве первого аргумента.
Это может быть название функции:
Переменная, которая обращается к функции:
Или же анонимная функция:
Но я не рекомендую так делать по следующим причинам:
- Такой код плохо воспринимается, а, следовательно, его сложно будет модернизировать или отладить;
- Он предполагает использование метода eval() , который может стать потенциальной уязвимостью;
- Этот метод работает медленнее других, так как ему требуется запускать JavaScript-интерпретатор .
Также обратите внимание, что для тестирования кода мы используем метод alert для JavaScript timeout .
Передаем параметры в setTimout
В первом ( к тому же, кроссбраузерном ) варианте мы передаем параметры в callback-функцию , исполняемую при помощи setTimeout .
В следующем примере мы выделяем случайное приветствие из массива greetings и передаем его в качестве параметра функции greet() , которая исполняется setTimeout с задержкой в 1 секунду:
Альтернативный метод
В синтаксисе, приведенном в начале статьи, существует еще один метод, с помощью которого можно передать параметры в callback-функцию , исполняемую JavaScript timeout . Данный метод подразумевает под собой вывод всех параметров, следующих после задержки.
Опираясь на предыдущий пример, мы получаем:
Этот метод не будет работать в IE 9 и ниже, где передаваемые параметры расцениваются как undefined . Но для решения этой проблемы на MDN есть специальный полифилл .
Сопутствующие проблемы и “this”
Код, исполняемый setTimeout , запускается отдельно от функции, которой он был вызван. Из-за этого мы сталкиваемся с определенными проблемами, в качестве решения которых можно использовать ключевое слово this .
Причина такого вывода кроется в том, что в первом примере this ведет к объекту person , а во втором примере — указывает на глобальный объект window , у которого отсутствует свойство firstName .
Чтобы избавиться от этой нестыковки, можно воспользоваться несколькими методами:
Принудительно установить значение this
Это можно сделать при помощи bind() – метода, который создает новую функцию, которая при вызове в качестве значения ключа this использует определенное значение. В нашем случае — указанный объект person . Это в результате дает нам:
Примечание: метод bind был представлен в ECMAScript 5 , а значит, что он будет работать только в современных браузерах. В других при его применении вы получите ошибку выполнения JavaScript « function timeout error » .
Использовать библиотеку
Многие библиотеки включают в себя встроенные функции, необходимые для решения проблемы с this . Например, метод jQuery.proxy() . Он берет функцию и возвращает новую, в которой всегда будет использовать определенный контекст. В нашем случае, контекстом будет:
Отключение таймера
Возвращенное значение setTimeout представляет собой числовой id , который можно использовать для отключения таймера при помощи функции clearTimeout() :
Давайте посмотрим на нее в действии. В следующем примере, если кликнуть по кнопке « Start countdown », начнется обратный отсчет. После того, как он завершится, котята получат свое. Но если нажать кнопку « Stop countdown », таймер JavaScript timeout будет остановлен и сброшен.
Подведем итоги
setTimeout – асинхронная функция, а это значит, что полученное обращение к этой функции попадает в очередь, и будет исполнено только после того, как завершатся все остальные действия в стеке. Она не может работать одновременно с другими функциями или отдельным потоком:
Многие также путают использование JavaScript-функции setTimeout , и jQuery-метода delay . Метод delay предназначен для установки временной задержки между методами в заданной очереди jQuery . Задержку отметить невозможно.
К примеру, если вы хотите затемнить изображение на секунду, сделать его видимым на 5 секунд, а затем снова затемнить на 1 секунду, то придется сделать следующее:
Все же лучше использовать JavaScript timeout для чего-то другого.
Если вам нужно повторить ранее использованный код после временной задержки, то для этих целей более уместной будет функция setInterval .
В завершение
В этой статье я попытался показать, как использовать setTimeout для отсрочки исполнения функции. К тому же я рассказал, как вносить параметры в setTimeout , работать со значением this внутри callback-функции и об отмене таймера.
Данная публикация представляет собой перевод статьи « jQuery setTimeout() Function Examples » , подготовленной дружной командой проекта Интернет-технологии.ру
setInterval вызов одной функции с разными параметрами
Подскажите, пожалуйста, как нужно каждые 5 секунд вызывать одну функцию с разными параметрами.
Javascript | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
29.05.2012, 13:23 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Как работает циклический вызов setInterval? Конструктор с разными параметрами SetInterval ,2 функции setInterval co значениями внутри функции |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
29.05.2012, 14:16 | 2 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|