If you are familiar with client-side JavaScript programming, you may have used the setTimeout and setInterval functions, which allow delays for a period of time before running the function. For example, the following code, once loaded on the web page, after 1 second, "Hello there" will be added after the page document:
The code copy is as follows:
var oneSecond = 1000 * 1; // one second = 1000 x 1 ms
setTimeout(function() {
document.write('<p>Hello there.</p>');
}, oneSecond);
and setInterval allows repeated execution of functions at specified time intervals. If the following code is injected into the web page, it will cause the following sentence "Hello there" to be added to the page document every second:
The code copy is as follows:
var oneSecond = 1000 * 1; // one second = 1000 x 1 ms
setInterval(function() {
document.write('<p>Hello there.</p>');
}, oneSecond);
Because the Web has long become a platform for building applications, rather than a simple static page, this kind of similar demand is increasingly emerging. These task planning functions help developers implement periodic verification of forms, delay remote data synchronization, or UI interactions that require delayed responses. Node also implements these methods in full. On the server side, you can use them to repeat or delay execution of many tasks, such as cache expiration, connection pool cleaning, session expiration, polling, and more.
Execute using setTimeout delay function
setTimeout can create an execution plan that runs the specified function once at a certain time in the future, such as:
The code copy is as follows:
var timeout_ms = 2000; // 2 seconds
var timeout = setTimeout(function() {
console.log("timed out!");
}, timeout_ms);
Like client JavaScript, setTimeout accepts two parameters. The first parameter is the function that needs to be delayed, and the second parameter is the delay time (in milliseconds).
setTimeout returns a timeout handle, which is an internal object. You can use it as a parameter to call clearTimeout to cancel the timer. Except that, this handle has no effect.
Use clearTimeout to cancel the execution plan
Once the timeout handle is obtained, you can use clearTimeout to cancel the function execution plan, like this:
The code copy is as follows:
var timeoutTime = 1000; // one second
var timeout = setTimeout(function() {
console.log("timed out!");
}, timeoutTime);
clearTimeout(timeout);
In this example, the timer will never be triggered and will not output the words "time out!". You can also cancel the execution plan at any time in the future, like the following example:
The code copy is as follows:
var timeout = setTimeout(function A() {
console.log("timed out!");
}, 2000);
setTimeout(function B() {
clearTimeout(timeout);
}, 1000);
The code specifies two functions A and B that are executed delayed. Function A is planned to be executed after 2 seconds, and B is planned to be executed after 1 second, because function B is executed first, and it cancels A's execution plan, so A will never run.
Develop and cancel the repetitive execution plan of the function
setInterval is similar to setTimeout, but it will repeatedly execute a function at a specified time interval. You can use it to periodically trigger a program to complete some other tasks that need to be repeated, such as cleaning, collecting, logging, getting data, polling, etc.
The following code will output a "tick" to the console every second:
The code copy is as follows:
var period = 1000; // 1 second
setInterval(function() {
console.log("tick");
}, period);
If you don't want it to run forever, you can use clearInterval() to cancel the timer.
setInterval returns an execution plan handle, which can be used as a parameter to clearInterval to cancel the execution plan:
The code copy is as follows:
var interval = setInterval(function() {
console.log("tick");
}, 1000);
// …
clearInterval(interval);
Use process.nextTick to delay function execution to the next round of the event loop
Sometimes the client JavaScript programmer uses setTimeout(callback,0) to delay the task for a short period of time. The second parameter is 0 milliseconds. It tells JavaScript to execute this callback function immediately after all pending events are processed. Sometimes this technique is used to delay execution of operations that do not need to be performed immediately. For example, sometimes you need to start playing animations or do some other calculations after the user event is processed.
In Node, just like the literal meaning of "event loop", the event loop runs in a loop that handles event queues, and each round in the event loop work is called a tick.
You can call the callback function once every time the event loop starts the next round (next tick) execution, which is exactly the principle of process.nextTick, and setTimeout, setTimeout uses the execution queue inside the JavaScript runtime, instead of using the event loop.
By using process.nextTick(callback) instead of setTimeout(callback, 0), your callback function will be executed immediately after the event in the queue is processed. It is much faster than the JavaScript timeout queue (measured by CPU time).
You can delay the function until the next event loop and run it like this:
The code copy is as follows:
process.nextTick(function() {
my_expensive_computation_function();
});
Note: The process object is one of the few global objects in Node.
Blocking event loop
The runtime of Node and JavaScript uses a single-threaded event loop. Each loop, the runtime uses the callback function to handle the next event in the queue. When the event is executed, the event loop obtains the execution result and processes the next event, repeating this until the event queue is empty. If one of the callbacks takes a long time to run, the event loop cannot handle other pending events during that time, which can make the application or service very slow.
When processing events, if memory-sensitive or processor-sensitive functions are used, the event loop will become slow, and a large number of events will accumulate, which cannot be processed in time, or even block the queue.
See the following example of blocking event loop:
The code copy is as follows:
process.nextTick(function nextTick1() {
var a = 0;
while(true) {
a ++;
}
});
process.nextTick(function nextTick2() {
console.log("next tick");
});
setTimeout(function timeout() {
console.log("timeout");
}, 1000);
In this example, nextTick2 and timeout functions have no chance to run no matter how long they wait, because the event loop is blocked by the infinite loop in the nextTick function, and it will not run even if the timeout function is scheduled to be executed after 1 second.
When setTimeout is used, the callback functions are added to the execution plan queue, and in this case they are not even added to the queue. Although this is an extreme example, you can see that running a processor-sensitive task can block or slow down the event loop.
Exit the event loop
Using process.nextTick, a non-critical task can be postponed to the next round of the event loop (tick) before execution, which can free the event loop so that it can continue to execute other pending events.
See the following example. If you plan to delete a temporary file but don't want the callback function of the data event to wait for this IO operation, you can delay it like this:
The code copy is as follows:
stream.on("data", function(data) {
stream.end("my response");
process.nextTick(function() {
fs.unlink("/path/to/file");
});
});
Use setTimeout instead of setInterval to ensure seriality of function execution
Suppose you are planning to design a function called my_async_function, which can do some I/O operations (such as parsing log files) and intend to make it execute periodically. You can implement it with setInterval like this:
The code copy is as follows:
var interval = 1000;
setInterval(function() {
my_async_function(function() {
console.log('my_async_function finished!');
});
},interval);//Translator's note: I added the previous ",interval" and the author should have missed it because of a typo
You must be able to make sure that these functions are not executed simultaneously, but if you use setinterval you cannot guarantee this. If the my_async_function function runs one millisecond longer than the interval variables, they will be executed simultaneously, rather than serially in sequence.
Translator's note: (The bold part below is added by the translator, not the content of the original book)
To facilitate understanding of this part of the content, you can modify the author's code so that it can actually run:
The code copy is as follows:
var interval = 1000;
setInterval(function(){
(function my_async_function(){
setTimeout(function(){
console.log("1");
},5000);
})();
},interval);
Run this code and you will find that after waiting for 5 seconds, "hello" is output every 1 second. We expect that after the current my_async_function has been executed (taken 5 seconds), wait 1 second before executing the next my_async_function, and the interval between each output should be 6 seconds. This result is because my_async_function is not executed serially, but is run at the same time.
Therefore, you need a way to force the interval between the end of an execution of a my_async_function and the start of the execution of the next my_async_function is exactly the time specified by the interval variable. You can do this:
The code copy is as follows:
var interval = 1000; // 1 second
(function schedule() { //Line 3
setTimeout(function do_it() {
my_async_function(function() { //Line 5
console.log('async is done!');
schedule();
});
}, interval);
}()); //Line 10
In the previous code, a function called schedule (line 3) is declared, and it is called immediately after declaration (line 10). The schedule function will run the do_it function after 1 second (specified by interval). After 1 second, the my_async_function function on line 5 will be called. When it is executed, it will call its own anonymous callback function (line 6). This anonymous callback function will reset the execution plan of do_it again and let it be executed again after 1 second, so that the code will start serially and continuously execute.
summary
You can use the setTimeout() function to preset the execution plan of the function and cancel it with the clearTimeout() function. You can also use setInterval() to execute a certain function periodically repeatedly. Accordingly, you can use clearInterval() to cancel this repeated execution plan.
If the event loop is blocked by using a processor-sensitive operation, the functions that were originally planned to be executed will be delayed and will never be executed. So don't use CPU-sensitive operations within the event loop. Also, you can use process.nextTick() to delay the execution of the function until the next round of the event loop.
When using I/O and setInterval() together, you cannot guarantee that there is only one pending call at any point in time, but you can use recursive functions and setTimeout() functions to avoid this tricky problem.