JavaScript timers: all you need to know
- Transfer
Hello colleagues. A long time ago, an article by H. Rezig was written on Habré on this subject. 10 years have passed, and the topic still needs clarification. Therefore, we suggest those interested in reading the article by Samer Buna, which not only provides a theoretical overview of the timers in JavaScript (in the context of Node.js), but also the tasks for them.
A few weeks ago, I tweeted the following question from a single interview:
*** Answer it for yourself, and then read on ***
About half of the responses to this tweet were incorrect. No, the case is NOT CONNECTED with V8 (or other VM) !!! Functions like
In browsers, the main function-timers relate to the interface
Node timers are part of the object.
It may seem to someone that this is just a bad question from an interview - what is the use of knowing this? I, as a JavaScript developer, think so: it is assumed that you should know this, because the opposite may indicate that you do not quite understand how V8 (and other virtual machines) interact with browsers and Node.
Consider a few examples and solve a couple of tasks on timers, let's go?
To run the examples in this article, you can use the node command. Most of the examples reviewed here appear in my Getting Started with Node.js course at Pluralsight.
Deferred function
Timers are higher-order functions with which you can postpone or repeat the execution of other functions (the timer receives such a function as the first argument).
Here is an example of deferred execution:
In this example, the
The first argument
If you execute the file
Note: the first argument
Passing Arguments
If the function for which delay is used
Here is an example:
The above function
When the
The task for timers # 1.
So, based on the material we have already studied about
Restriction
In your solution, you can define only one function containing built-in functions. This means that multiple calls
Solution
This is how I would solve this problem:
I
Then I used
Having executed the file
Repeat the execution of the function.
And what if I asked you to display a message every 4 seconds, indefinitely?
Of course, you can enclose it
Here is an example
This code will display a message every 3 seconds. If executed with a
Canceling Timers
Because an action is assigned when you call a timer function, this action can also be canceled before it is executed.
The call
This simple timer should fire after 0 ms (that is, immediately), but this will not happen, because we capture the value
When executed
By the way, in Node.js there is another way to set it
This feature
Along with
Timer delay is not a guaranteed thing.
You noticed that in the previous example, when performing an operation with
Let me explain this point with an example. Here is a simple call
Immediately after determining the timer in this example, we synchronously block the runtime environment with a large loop
Of course, in practice, doing so is very bad, but this example helps to understand that the delay
Task for timers # 2
Write a script that will display the message “Hello World” once a second, but only 5 times. After 5 iterations, the script should display the message “Done”, after which the Node process will end.
Restriction : in solving this problem can not be called
Hint : need a counter.
Solution
This is how I would solve this problem:
The deferred function will display a message and each time increase the counter by one. Inside the deferred function, we have an if statement that will check if 5 iterations have passed. After 5 iterations, the program displays “Done” and clears the interval value using the captured constant
"Who" exactly causes deferred functions?
When using the JavaScript keyword
the value in the keyword
Let's define a function as a property of an object so that it becomes a little clearer:
Now, when dealing with a function,
And now the question is: who will be the caller if you send the link to the
Who is the caller in this case?
The answer will differ depending on where the timer function is performed. In this case, the dependence on who is the caller is simply unacceptable. You will lose control of the caller, since it is up to the implementation of the timer that will determine who in this case calls your function. If you test this code in a Node REPL, then the caller will be the object
Note: this is important only if the JavaScript keyword is
Timer Challenge # 3
Write a script that will continuously display the “Hello World” message with varying delays. Start with a one-second delay, then increase it by one second at each iteration. On the second iteration, the delay will be 2 seconds. In the third - three, and so on.
Include a delay in the displayed message. You should get something like this: Constraints : variables can only be defined with const. Using let or var is impossible. Solution Since the duration of the delay in this task is variable, you cannot use it here, but you can manually adjust the interval execution with the help of a recursive call. The first function executed with will create the next timer, and so on.
In addition, since you cannot use
Here's how to solve this problem:
Task for timers # 4
Write a script that will display the message “Hello World” with the same delay structure as in task # 3, but this time in groups of 5 messages, and there will be a main delay interval in the groups. For the first group of 5 messages, we select an initial delay of 100 ms, for the next one - 200 ms, for the third one - 300 ms, and so on.
Here’s how this script should work:
According to this principle, the program should work indefinitely.
Include a delay in the displayed message. You should get something like this (without comments): Restrictions : Only calls (and not ) and only ONE instructions can be used . Solution Since we can only work with calls , here we will need to use recursion, as well as increase the delay of the next call . In addition, we will need the instruction to make this happen only after 5 calls to this recursive function. Here is a possible solution:
Thanks to everyone who read it.
A few weeks ago, I tweeted the following question from a single interview:
“Where is the source code for the setTimeout and setInterval functions? Where would you look for it? You can not google :) "
*** Answer it for yourself, and then read on ***
About half of the responses to this tweet were incorrect. No, the case is NOT CONNECTED with V8 (or other VM) !!! Functions like
setTimeout
and setInterval
, proudly referred to as "JavaScript Timers", are not included in any ECMAScript specification or implementation of the JavaScript engine. Timer functions are implemented at the browser level, so their implementation differs in different browsers. Also, timers are natively implemented in the Node.js runtime itself. In browsers, the main function-timers relate to the interface
Window
, also associated with some other functions and objects. This interface provides global access to all its elements in the main JavaScript scope. That is why the function setTimeout
can be performed directly in the browser console. Node timers are part of the object.
global
which is arranged like a browser interface Window
. The source code for the timers in Node is shown here . It may seem to someone that this is just a bad question from an interview - what is the use of knowing this? I, as a JavaScript developer, think so: it is assumed that you should know this, because the opposite may indicate that you do not quite understand how V8 (and other virtual machines) interact with browsers and Node.
Consider a few examples and solve a couple of tasks on timers, let's go?
To run the examples in this article, you can use the node command. Most of the examples reviewed here appear in my Getting Started with Node.js course at Pluralsight.
Deferred function
Timers are higher-order functions with which you can postpone or repeat the execution of other functions (the timer receives such a function as the first argument).
Here is an example of deferred execution:
// example1.js
setTimeout(
() => {
console.log('Hello after 4 seconds');
},
4 * 1000
);
In this example, the
setTimeout
output of the greeting message is delayed by 4 seconds. The second argument setTimeout
is the delay (in ms). I multiply 4 by 1000 to get 4 seconds. The first argument
setTimeout
is a function whose execution will be postponed. If you execute the file
example1.js
with the node command, the Node pauses for 4 seconds and then displays a welcome message (after which the output will follow). Note: the first argument
setTimeout
is just a function reference . It should not be a built-in function - such as example1.js
. Here is the same example without using the built-in function:const func = () => {
console.log('Hello after 4 seconds');
};
setTimeout(func, 4 * 1000);
Passing Arguments
If the function for which delay is used
setTimeout
accepts any arguments, you can use the remaining arguments of the function itself setTimeout
(after those 2 that we have already studied) to transfer the values of the arguments to the deferred function.// Для: func(arg1, arg2, arg3, ...)// Можно использовать: setTimeout(func, delay, arg1, arg2, arg3, ...)
Here is an example:
// example2.jsconst rocks = who => {
console.log(who + ' rocks');
};
setTimeout(rocks, 2 * 1000, 'Node.js');
The above function
rocks
, deferred for 2 seconds, takes an argument who
, and the call setTimeout
passes it the value of “Node.js” as such an argument who
. When the
example2.js
command executes the node
phrase “Node.js rocks” will be displayed in 2 seconds. The task for timers # 1.
So, based on the material we have already studied about
setTimeout
, we derive 2 following messages after the corresponding delays.- The message “Hello after 4 seconds” is displayed after 4 seconds.
- The message “Hello after 8 seconds” is displayed after 8 seconds.
Restriction
In your solution, you can define only one function containing built-in functions. This means that multiple calls
setTimeout
will need to use the same function. Solution
This is how I would solve this problem:
// solution1.jsconst theOneFunc = delay => {
console.log('Hello after ' + delay + ' seconds');
};
setTimeout(theOneFunc, 4 * 1000, 4);
setTimeout(theOneFunc, 8 * 1000, 8);
I
theOneFunc
get an argument delay
and uses the value of this argument delay
in the message displayed on the screen. Thus, the function can display different messages depending on the delay value we will tell it. Then I used
theOneFunc
two calls setTimeout
, and the first call works after 4 seconds, and the second after 8 seconds. Both of these calls setTimeout
also get the 3rd argument representing the argument delay
for theOneFunc
. Having executed the file
solution1.js
with the node command, we will display the task requirements; moreover, the first message will appear after 4 seconds, and the second after 8 seconds. Repeat the execution of the function.
And what if I asked you to display a message every 4 seconds, indefinitely?
Of course, you can enclose it
setTimeout
in a cycle, but the Timer API also offers a function setInterval
with which you can program the “eternal” execution of some operation. Here is an example
setInterval
:// example3.js
setInterval(
() =>console.log('Hello every 3 seconds'),
3000
);
This code will display a message every 3 seconds. If executed with a
example3.js
command node
, Node will output this command until you forcibly terminate the process (CTRL + C). Canceling Timers
Because an action is assigned when you call a timer function, this action can also be canceled before it is executed.
The call
setTimeout
returns the timer ID, and you can use this timer ID when calling clearTimeout
to cancel the timer. Here is an example:// example4.jsconst timerId = setTimeout(
() =>console.log('You will not see this one!'),
0
);
clearTimeout(timerId);
This simple timer should fire after 0 ms (that is, immediately), but this will not happen, because we capture the value
timerId
and immediately cancel the timer with a call clearTimeout
. When executed
example4.js
by a command node
, Node will not print anything - the process will simply end immediately. By the way, in Node.js there is another way to set it
setTimeout
with a value of 0 ms. The Node.js Timers API has another function called setImmediate
, and it basically does the same thing as setTimeout
with 0 ms, but in this case, the delay can be omitted:setImmediate(
() =>console.log('I am equivalent to setTimeout with 0 ms'),
);
This feature
setImmediate
is not supported in all browsers . Do not use it in the client code. Along with
clearTimeout
there is a function clearInterval
that does the same thing, but with calls setInerval
, and there is also a call clearImmediate
. Timer delay is not a guaranteed thing.
You noticed that in the previous example, when performing an operation with
setTimeout
after 0 ms, this operation does not occur immediately (after setTimeout
), but only after all the script code (including the call clearTimeout
) has been completely executed ? Let me explain this point with an example. Here is a simple call
setTimeout
that should work out in half a second - but this does not happen:// example5.js
setTimeout(
() =>console.log('Hello after 0.5 seconds. MAYBE!'),
500,
);
for (let i = 0; i < 1e10; i++) {
// Синхронно блокируем операции
}
Immediately after determining the timer in this example, we synchronously block the runtime environment with a large loop
for
. The value 1e10
is 1 with 10 zeros, so the cycle lasts 10 billion processor cycles (in principle, this is how the overloaded processor is simulated). Node can do nothing until this loop ends. Of course, in practice, doing so is very bad, but this example helps to understand that the delay
setTimeout
is not a guaranteed, but rather a minimum value . A value of 500 ms means that the delay will last at least 500 ms. In fact, the script will take much more time to display the welcome line on the screen. First, he will have to wait until the blocking cycle is completed. Task for timers # 2
Write a script that will display the message “Hello World” once a second, but only 5 times. After 5 iterations, the script should display the message “Done”, after which the Node process will end.
Restriction : in solving this problem can not be called
setTimeout
. Hint : need a counter.
Solution
This is how I would solve this problem:
let counter = 0;
const intervalId = setInterval(() => {
console.log('Hello World');
counter += 1;
if (counter === 5) {
console.log('Done');
clearInterval(intervalId);
}
}, 1000);
counter
I set the
initial value to 0, and then called setInterval
, taking its id. The deferred function will display a message and each time increase the counter by one. Inside the deferred function, we have an if statement that will check if 5 iterations have passed. After 5 iterations, the program displays “Done” and clears the interval value using the captured constant
intervalId
. The delay interval is 1000 ms. "Who" exactly causes deferred functions?
When using the JavaScript keyword
this
inside a normal function, like this:functionwhoCalledMe() {
console.log('Caller is', this);
}
the value in the keyword
this
will match the caller . If you define the above function inside the Node REPL, then the object will call it global
. If you define a function in the browser console, then the object will call it window
. Let's define a function as a property of an object so that it becomes a little clearer:
const obj = {
id: '42',
whoCalledMe() {
console.log('Caller is', this);
}
};
// Теперь ссылка на функцию такова: obj.whoCallMe
Now, when dealing with a function,
obj.whoCallMe
we will directly use the link to it, the object will be the caller obj
(identifiable in its own way id
): And now the question is: who will be the caller if you send the link to the
obj.whoCallMe
call setTimetout
?// Какой текст будет выведен в данном случае??
setTimeout(obj.whoCalledMe, 0);
Who is the caller in this case?
The answer will differ depending on where the timer function is performed. In this case, the dependence on who is the caller is simply unacceptable. You will lose control of the caller, since it is up to the implementation of the timer that will determine who in this case calls your function. If you test this code in a Node REPL, then the caller will be the object
Timeout
: Note: this is important only if the JavaScript keyword is
this
used inside normal functions. When using switch functions, the caller should not bother you at all. Timer Challenge # 3
Write a script that will continuously display the “Hello World” message with varying delays. Start with a one-second delay, then increase it by one second at each iteration. On the second iteration, the delay will be 2 seconds. In the third - three, and so on.
Include a delay in the displayed message. You should get something like this: Constraints : variables can only be defined with const. Using let or var is impossible. Solution Since the duration of the delay in this task is variable, you cannot use it here, but you can manually adjust the interval execution with the help of a recursive call. The first function executed with will create the next timer, and so on.
Hello World. 1
Hello World. 2
Hello World. 3
...
setInterval
setTimeout
setTimeout
In addition, since you cannot use
let
/ var
, we cannot have a counter to increment the delay on each recursive call; instead, you can use the arguments of the recursive function to increment during a recursive call. Here's how to solve this problem:
const greeting = delay =>
setTimeout(() => {
console.log('Hello World. ' + delay);
greeting(delay + 1);
}, delay * 1000);
greeting(1);
Task for timers # 4
Write a script that will display the message “Hello World” with the same delay structure as in task # 3, but this time in groups of 5 messages, and there will be a main delay interval in the groups. For the first group of 5 messages, we select an initial delay of 100 ms, for the next one - 200 ms, for the third one - 300 ms, and so on.
Here’s how this script should work:
- At a mark of 100 ms, the script displays the “Hello World” for the first time, and does so 5 times with an interval increasing by 100 ms. The first message will appear after 100 ms, the second after 200 ms, etc.
- After the first 5 messages, the script should increase the main delay by as much as 200 ms. Thus, the 6th message will be output in 500 ms + 200 ms (700 ms), the 7th message - 900 ms, the 8th message - in 1100 ms, and so on.
- After 10 messages, the script should increase the main delay interval by 300 ms. The 11th message should be output after 500 ms + 1000 ms + 300 ms (18000 ms). The 12th message should be output after 2100 ms, etc.
According to this principle, the program should work indefinitely.
Include a delay in the displayed message. You should get something like this (without comments): Restrictions : Only calls (and not ) and only ONE instructions can be used . Solution Since we can only work with calls , here we will need to use recursion, as well as increase the delay of the next call . In addition, we will need the instruction to make this happen only after 5 calls to this recursive function. Here is a possible solution:
Hello World. 100 // При 100 мс
Hello World. 100 // При 200 мс
Hello World. 100 // При 300 мс
Hello World. 100 // При 400 мс
Hello World. 100 // При 500 мс
Hello World. 200 // При 700 мс
Hello World. 200 // При 900 мс
Hello World. 200 // При 1100 мс
...
setInterval
setTimeout
if
setInterval
setInterval
if
let lastIntervalId, counter = 5;
const greeting = delay => {
if (counter === 5) {
clearInterval(lastIntervalId);
lastIntervalId = setInterval(() => {
console.log('Hello World. ', delay);
greeting(delay + 100);
}, delay);
counter = 0;
}
counter += 1;
};
greeting(100);
Thanks to everyone who read it.