Kodeclik Blog
How to add time delays to Javascript
Sometimes you might need to add delays to your Javascript program. This could be for user experience improvements, for instance when you want to display a notification for or after a certain duration, or when you want to create an animation or transition with timed steps. Let us learn how to do so!
Using the setTimeout() function for delays
To implement delays, you can use the setTimeout() function. This function allows you to execute a piece of code after a specified delay. The basic syntax for setTimeout() is:
setTimeout(function, delay);
Here "function" is the code you want to execute, and "delay" is the time in milliseconds before the function is executed.
For example, to delay a console log by 3 seconds:
setTimeout(() => {
console.log("This message appears after 3 seconds");
}, 3000);
Delaying Multiple Actions
If you need to delay multiple actions in sequence, you can nest setTimeout() calls:
console.log("First message");
setTimeout(() => {
console.log("Second message after 2 seconds");
setTimeout(() => {
console.log("Third message after another 3 seconds");
}, 3000);
}, 2000);
Using setTimeout() with Functions
You can also use setTimeout() with named functions:
function delayedMessage() {
console.log("This message is delayed");
}
setTimeout(delayedMessage, 2000);
Canceling a Timeout
If you need to cancel a scheduled timeout, you can use clearTimeout():
const timeoutID = setTimeout(() => {
console.log("This won't be executed");
}, 5000);
clearTimeout(timeoutID);
Why Javascript’s asynchronous execution model interferes with setTimeout()
Remember that Javascript is single-threaded and asynchronous. While a delay is in progress, other code can still execute. This non-blocking behavior is crucial for creating responsive web applications but can sometimes lead to unexpected behavior when using setTimeout(), especially when combined with other asynchronous operations like API calls.
Let's explore this with an example:
console.log("Start of script");
// Simulated API call using fetch
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
console.log("API call completed:", data);
})
.catch(error => {
console.error("API call failed:", error);
});
// setTimeout
setTimeout(() => {
console.log("Timeout completed");
}, 0);
console.log("End of script");
The output of this script will likely be:
Start of script
End of script
Timeout completed
API call completed: [data from API]
This is really strange! Why does this happen? Read on!
In the above code, the script starts executing synchronously, logging "Start of script". The fetch() API call is initiated. This is an asynchronous operation, so JavaScript doesn't wait for it to complete before moving on. The setTimeout() is set up, even with a delay of 0 ms. It's queued to run after the current execution context is complete. "End of script" is logged. The main execution context is now complete, so JavaScript checks the event queue. The setTimeout callback is ready to execute (since we set it to 0 ms), so it runs, logging "Timeout completed". When the API call finally completes (which could take hundreds of milliseconds or more), its .then() callback executes, logging the API data.
This behavior occurs because API calls are typically slower than the minimal delay of setTimeout(). setTimeout() with a delay of 0 doesn't execute immediately; it waits for the current execution context to complete. The event loop prioritizes timer callbacks (like setTimeout) over I/O callbacks (like API responses) in most cases.
If the above behavior is unsettling, and you wish to ensure that code runs after both the API call and the timeout are complete, you could nest the setTimeout() inside the API call's .then() block, or use async/await with Promise.all() to wait for both operations to complete. The choice depends on your specific requirements and the desired execution order.
Printing multiple words after subsequent delays
To drive home the point, here is an even simpler example. Let us print each word of “Kodeclik Online Academy” after a small delay.
console.log('Start');
setTimeout(() => {
console.log('Kodeclik');
setTimeout(() => {
console.log('Online');
setTimeout(() => {
console.log('Academy');
}, 1000);
}, 1000);
}, 1000);
console.log('End');
The output will likely be:
Start
End
(1 second delay)
Kodeclik
(1 second delay)
Online
(1 second delay)
Academy
In this example: 'Start' is logged immediately. 'End' is logged immediately after 'Start'. After a 1-second delay, 'Kodeclik' is logged. 1 second after 'Kodeclik', 'Online' is logged. Finally, 1 second after 'Online', 'Academy' is logged.
This example easily demonstrates how setTimeout() creates asynchronous behavior, allowing the immediate execution of synchronous code ('Start' and 'End') while delaying the execution of the nested setTimeout() callbacks. Each word of "Kodeclik Online Academy" is printed with a 1-second delay between them, creating a staggered output effect.
Printing each character of the alphabet with a progressive delay
Here's an example that uses a similar structure to print each character of the alphabet with a delay:
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
for (let i = 0; i < alphabet.length; i++) {
setTimeout(() => {
console.log(alphabet[i]);
}, 1000 * (i + 1));
}
We define a string alphabet containing all uppercase letters of the English alphabet. We use a for loop to iterate through each character of the alphabet string. For each character, we set up a setTimeout function but with a separate delay. This means that the first character (A) will be printed after 1 second, the second character (B) will be printed after 2 seconds, and so on, with each subsequent character being printed 1 second after the previous one.
Note that the use of let in the loop declaration ensures that each iteration has its own scope for the variable i, which is crucial for the correct functioning of the setTimeout callbacks. When you run this code, you'll see each letter of the alphabet printed to the console, one per second, starting one second after the code begins execution. The entire process will take 26 seconds to complete.
This example demonstrates how to use setTimeout within a loop to create a sequence of delayed actions, each with an increasing delay.
The wrong way to add progressive delays
If on the other hand you had written:
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
for (let i = 0; i < alphabet.length; i++) {
setTimeout(() => {
console.log(alphabet[i]);
}, 1000);
}
console.log("Loop finished");
This code has a slightly different semantics. The for loop will iterate 26 times (the length of the alphabet string) like before. For each iteration, a setTimeout function is scheduled with a delay of 1000 milliseconds (1 second). But the loop will finish very quickly, scheduling all 26 setTimeout calls almost instantaneously. "Loop finished" will be logged to the console immediately after the loop completes. After approximately 1 second from the start of the script execution, all 26 letters of the alphabet will be logged to the console almost simultaneously!
The key point to note here is that all setTimeout calls are scheduled with the same delay (1000 ms), unlike the previous example where we had increasing delays. JavaScript's event loop and the asynchronous nature of setTimeout mean that the loop finishes executing and schedules all the timeouts before any of them actually run. After the specified delay (1 second), all the scheduled callbacks will be ready to execute, and they will run in quick succession.
The output will look something like this:
Loop finished
(After about 1 second)
A
B
C
D
...
X
Y
Z
All the letters will appear almost at once, about a second after the script starts, rather than being spaced out over time. This behavior demonstrates how setTimeout schedules tasks for future execution without blocking the main thread, allowing the loop to complete quickly while deferring the actual logging of the letters.
Adding time delays by blocking the main thread
Suppose you do indeed wish to block the main execution thread and in this manner introduce delays into your program. How do you accomplish this?
If you wish to intentionally block the main execution thread and introduce delays in JavaScript, you can use a busy-wait loop. However, it's important to note that this is generally not recommended in most situations, as it can make your application unresponsive. Here's how you can do it:
function sleep(milliseconds) {
const start = Date.now();
while (Date.now() - start < milliseconds) {
// This empty loop will block execution
}
}
This function above creates a function that runs a loop for a specified amount of time.
You can use this function in this manner:
console.log("Start");
sleep(2000); // Blocks for 2 seconds
console.log("After 2 seconds");
sleep(1000); // Blocks for 1 second
console.log("After 3 seconds");
This approach will indeed block the main thread and introduce delays. However, it has several drawbacks. It makes the entire application unresponsive during the delay. It's CPU-intensive, as it keeps the processor busy in a loop. Finally, it might not be precise, as the actual delay may vary slightly depending on system load.
Finally, there are ways to add delays by using Promises and async/await (with Promises) but these are beyond the scope of this article. We hope you have fun with the above strategies!
If you liked this blogpost, checkout how to add time delays to your Python program!
Want to learn Javascript with us? Sign up for 1:1 or small group classes.