setTimeout()
and other timer APIs in Node.jsEditor’s note: This article was last updated on 21 June 2023 to include additional information about Node.js timers, like error handling and throttling API requests.
Node.js has various built-in methods that make managing asynchronous tasks easier compared to other server-side programming languages. The event loop implements the asynchronous functionality of Node.js; it is a single-threaded, semi-infinite loop that executes I/O or timer callbacks sequentially using a set of queues.
The event loop has six different phases: timers, pending callbacks, idle or prepare, poll, check, and close callbacks. In this article, we’ll focus on the timers phase. We’ll review examples of setTimeout()
, clearTimeout()
, and a few other Node.js timer functions. Let’s get started!
Jump ahead:
window.setTimeout()
vs. arrow functions with setTimeout()
setTimeout()
recursivelyThe Node.js timer module contains functions that run code after a set amount of time. Timer methods are globally available to imitate the browser JavaScript API, and therefore, they do not need to be imported via require()
.
setTimeout()
We can use setTimeout()
to set code execution after a certain period of time has passed in milliseconds. It is similar to the JavaScript API’s window.setTimeout()
function in browsers, however, you can’t pass a string of code to be executed. We’ll cover this in more detail later.
The setTimeout()
function takes three arguments:
Below is an example of the setTimeout()
function:
const myTimeout = setTimeout(myGreeting, 5000, "Pascal"); function myGreeting(arg) { console.log("Welcome back " + arg) }
Because of the setTimeout
call, the myGreeting()
function in the example above will execute after close to 5000
milliseconds, or five seconds. The execution of the timeout can be slightly delayed due to other executing codes that run in the event loop. The third parameter, Pascal
, is a string that is passed to the myGreeting()
function as its argument.
setTimeout()
returns a Timeout
object, which can be used to terminate the timeout using the clear
method, known as clearTimeout()
. One real life use case of a setTimeout()
function is a countdown to a flash sale in an ecommerce app.
setInterval()
setInterval()
causes an indefinite loop to be executed. It is most useful for repeating a piece of code with a millisecond delay. SetInterval()
, like setTimeout()
, requires three arguments:
The code below shows an example of the setInterval()
function:
function myInterval() { console.log('Catch me if you can!'); } setInterval(myInterval, 3000);
In the example above, the myInterval()
function will run every 3000
milliseconds, or three seconds, until it is stopped. Similar to setTimeout()
, the delay time may not be exact owing to other code operations that may occupy the event loop.
setInterval()
produces a Timeout
object that you can use to reference and adjust the previously set interval. One real life use case of the setInterval()
function is fetching the latest price of Ethereum at an interval of 60 seconds in a crypto trading app.
setImmediate()
The setImmediate()
function runs code after the current event loop cycle is completed. setImmediate()
is similar to setTimeout()
with a 0ms time delay. To put it another way, the setImmediate()
method will execute code after each statement in the script is executed.
setImmediate()
takes two arguments:
Below is an example of the setImmediate()
function:
console.log('before setImmediate'); const myImmediate = setImmediate((arg) => { console.log(`Hello and welcome ${arg}!`); }, 'Pascal'); console.log('After setImmediate'); for(let i=0; i<10; i++){ console.log(i); }
In the example above, the setImmediate()
function will run after all executable code has completed. The output will be as follows:
before immediate after immediate 0 1 2 3 4 5 6 7 8 9 Hello and welcome Pascal!
If you want to make a long, CPU-bound process async so that the event loop will continue to operate and handle other requests, setImmediate()
is a decent option.
setTimeout()
, setInterval()
, and setImmediate()
all return an object that can be used to refer to the Timeout
or Immediate
objects, respectively. Node.js provides clear
functions for stopping or cancelling the execution of the Timeout
or Immediate
objects.
clearTimeout()
The clearTimeout()
function is used to cancel the Timeout
object created by the setTimeout()
function:
const myTimeout = setTimeout(myGreeting, 5000, "Pascal"); function myGreeting(arg) { console.log("Welcome back " + arg) } clearTimeout(myTimeout)
clearInterval()
The clearInterval()
function halts the Interval
object created by the setInterval()
function:
function myInterval() { console.log('Catch me if you can!'); } setInterval(myInterval, 3000); clearInterval(myInterval)
clearImmediate()
The clearImmediate()
function stops the Immediate
objected returned by the setImmediate()
function:
console.log('before setImmediate'); const myImmediate = setImmediate((arg) => { console.log(`Hello and welcome ${arg}!`); }, 'Pascal'); console.log('After setImmediate'); for(let i=0; i<10; i++){ console.log(i); } clearImmediate(myImmediate);
window.setTimeout()
vs. arrow functions with setTimeout()
The scope and context of the callback function differ between using window.setTimeout()
and arrow functions with setTimeout()
. In web browsers, the window.setTimeout()
method is generally used to schedule a function to be run after a given delay. It works inside the context of the global window
object. On the other hand, when using arrow functions with setTimeout()
, the context of the arrow function is decided lexically. This signifies that the context within the arrow function is inherited from its scope.
By default, the callback function provided to window.setTimeout()
is not restricted to any particular context. If you use this
within a callback function, it will usually refer to the global window
object. Arrow functions inherit the lexical this
from the scope in which they are defined. As a result, if you use an arrow function as the callback in setTimeout()
, the value of this
will be the same as in the surrounding scope.
setTimeout()
recursivelyUsing setTimeout()
recursively is a JavaScript technique that allows you to schedule a function to execute repeatedly at a specified interval. By calling setTimeout()
instead of setInterval()
from within the callback function itself, you can create a recursive loop until a certain condition is met or an exit condition is triggered. The recursive approach to setTimeout()
means that the tasks can fully control when the delay starts.
Consider the following example:
function countDown(counter) { console.log(counter); if (counter === 0) { console.log('Count down completed.'); return; } setTimeout(() => { countDown(counter - 1); }, 1000); } countDown(10);
In the code above, we define a countDown()
callback function and pass a counter
argument. Within the function, the current counter
is logged to the console, followed by a condition to see if the counter
equals zero.
If the condition is true
, a statement is logged and the code stops running. If the condition is false
, the setTimeout()
function is called, and the countDown()
is run every 1000 milliseconds or one second. Here, the countDown()
function takes in the current counter
and subtracts one from it.
unref()
The unref()
timeout method notifies the Timeout
object that the Node.js event loop is no longer required. Because there is no other event keeping the event loop going in this situation, it will end before the callback to setTimeout()
is executed:
const myTimeout = setTimeout(myGreeting, 5000, "Pascal"); function myGreeting(arg) { console.log("Welcome back " + arg) } // Checking the myTimeout.hasRef() object console.log(myTimeout.hasRef()); // Unreferencing console.log(myTimeout.unref());
For the example above, the output will be as follows:
true Timeout { _idleTimeout: 5000, _idlePrev: [TimersList], _idleNext: [TimersList], _idleStart: 68, _onTimeout: [Function: myGreeting], _timerArgs: [Array], _repeat: null, _destroyed: false, [Symbol(refed)]: false, [Symbol(kHasPrimitive)]: false, [Symbol(asyncId)]: 2, [Symbol(triggerId)]: 1 }
The myGreeting()
function is not printed on the console. Notice the [Symbol(refed)]
is set to false
.
ref()
The ref()
method is called if the Timeout
object’s event loop is no longer active. The ref()
method is only used after timeout.unref()
has been called and you need to access the Timeout
object once more:
const myTimeout = setTimeout(myGreeting, 5000, "Pascal"); function myGreeting(arg) { console.log("Welcome back " + arg) } // Checking the myTimeout.hasRef() object console.log(myTimeout.hasRef()); // Unreferencing console.log(myTimeout.unref()); // Referencing again console.log(myTimeout.ref());
For the above example, the output will be as follows:
true Timeout { _idleTimeout: 5000, _idlePrev: [TimersList], _idleNext: [TimersList], _idleStart: 73, _onTimeout: [Function: myGreeting], _timerArgs: [Array], _repeat: null, _destroyed: false, [Symbol(refed)]: false, [Symbol(kHasPrimitive)]: false, [Symbol(asyncId)]: 2, [Symbol(triggerId)]: 1 } Timeout { _idleTimeout: 5000, _idlePrev: [TimersList], _idleNext: [TimersList], _idleStart: 73, _onTimeout: [Function: myGreeting], _timerArgs: [Array], _repeat: null, _destroyed: false, [Symbol(refed)]: true, [Symbol(kHasPrimitive)]: false, [Symbol(asyncId)]: 2, [Symbol(triggerId)]: 1 } Welcome back Pascal
The myGreeting()
function is printed on the console, and you’ll notice the [Symbol(refed)]
is set to true
.
Throttling API requests with Node.js timers can regulate the rate at which requests are sent to an API, thereby reducing excessive consumption or server overload. Let’s demonstrate a common technique for utilizing timers to achieve request throttling:
const desiredTimeInterval = 2000; //Milliseconds let lastRequestTimestamp = Date.now(); function makeRequest() { const currentTimestamp = Date.now(); const timeElapsed = currentTimestamp - lastRequestTimestamp; if (timeElapsed < desiredTimeInterval) { const delay = desiredTimeInterval - timeElapsed; setTimeout(makeRequest, delay); } else { // Make the API request here lastRequestTimestamp = Date.now(); } } makeRequest();
When the API call is successful, the lastRequestTimestamp
is updated to the current timestamp. When you wish to start an API request, invoke the makeRequest()
function, which will manage the throttling, and, if required, hold off the requests.
The code above will space out API calls based on the specified interval, preventing an overflow of requests and maintaining the defined rate limitation.
When using timers in Node.js, error handling is critical to guarantee that any possible errors or exceptions that arise during timer-based processes are handled appropriately. Let’s consider some important factors for dealing with timer errors.
It’s important to include a try...catch
block within the callback functions provided by timer APIs like setTimeout()
or setInterval()
. This lets you capture any synchronous errors that could arise during the timer callback execution.
If the timer callback contains asynchronous operations like API requests or database access, errors must be handled inside the asynchronous functions themselves. You can use promises to properly manage asynchronous errors.
If an error occurs and you want to end the timer, make sure you clear the timer using the proper function, either clearTimeout()
or clearInterval()
. This will stop the timer callback from being executed again.
When an error happens inside a timer callback or an asynchronous activity triggered by a timer, the error information must be logged for debugging. To log errors, you can utilize logging frameworks or Node.js’s built-in logging features like console.error()
. You should consider implementing error monitoring and reporting technologies to acquire insight into production problems.
In Node.js, we can use timers to execute a function at a certain time or to delay a program or code execution. In this article, we discussed the different timer functions available in Node.js to set or clear the execution of code. We also discussed two additional timer functions, unref()
and ref()
.
We then considered the benefits of using setTimeout()
recursively, comparing it to JavaScript’s window.setTimeout()
method. Lastly, we considered some best practices to approaching error handling with Node.js timer functions.
Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third-party services are successful, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]