Timing is everything in interactive web applications. JavaScript provides two essential timing functions: setTimeout() for executing code after a delay, and setInterval() for executing code repeatedly at fixed intervals. These functions are fundamental for creating animations, countdowns, polling mechanisms, auto-saves, and any feature that requires time-based behavior. Let's master JavaScript timing functions!
setTimeout() - Execute Once After Delay
setTimeout() schedules a function to run once after a specified delay (in milliseconds).
Basic Syntax
// Syntax: setTimeout(function, delay)
// delay is in milliseconds (1000ms = 1 second)
setTimeout(function() {
console.log('This runs after 2 seconds');
}, 2000);
// Arrow function syntax
setTimeout(() => {
console.log('This also runs after 2 seconds');
}, 2000);
// Named function
function sayHello() {
console.log('Hello after 1 second');
}
setTimeout(sayHello, 1000);
// Note: Don't call the function - no ()
// ✓ Correct: setTimeout(sayHello, 1000)
// ✗ Wrong: setTimeout(sayHello(), 1000) // Executes immediately!Passing Arguments to Callback
// Method 1: Additional parameters (after delay)
function greet(name, message) {
console.log(`${message}, ${name}!`);
}
setTimeout(greet, 1000, 'Alice', 'Hello');
// After 1 second: "Hello, Alice!"
// Method 2: Arrow function wrapper (more common)
setTimeout(() => {
greet('Bob', 'Hi');
}, 1000);
// Method 3: bind() to preset arguments
let greetAlice = greet.bind(null, 'Alice', 'Welcome');
setTimeout(greetAlice, 1000);
// Example with multiple arguments
function calculate(a, b, operation) {
let result = operation === 'add' ? a + b : a * b;
console.log(`Result: ${result}`);
}
setTimeout(calculate, 1500, 5, 3, 'add');
// After 1.5 seconds: "Result: 8"Returning Timeout ID
// setTimeout returns a timeout ID (number)
let timeoutId = setTimeout(() => {
console.log('This might not run');
}, 3000);
console.log('Timeout ID:', timeoutId); // e.g., 1, 2, 3...
// You can use this ID to cancel the timeout
// We'll see clearTimeout() next
// Multiple timeouts get different IDs
let id1 = setTimeout(() => console.log('First'), 1000);
let id2 = setTimeout(() => console.log('Second'), 2000);
let id3 = setTimeout(() => console.log('Third'), 3000);
console.log('IDs:', id1, id2, id3); // e.g., 1, 2, 3clearTimeout() - Cancel Scheduled Timeout
clearTimeout() cancels a timeout that was previously established by setTimeout().
// Schedule a timeout
let timeoutId = setTimeout(() => {
console.log('This will NOT run');
}, 3000);
// Cancel it before it executes
clearTimeout(timeoutId);
console.log('Timeout cancelled');
// Practical example: Cancel on user action
let messageTimeout = setTimeout(() => {
console.log('You have been inactive for 5 seconds');
}, 5000);
// User does something - cancel the timeout
document.addEventListener('click', function() {
clearTimeout(messageTimeout);
console.log('Activity detected - timeout cancelled');
});
// Example: Debouncing (covered in detail later)
let searchTimeout;
function search(query) {
// Clear previous timeout
clearTimeout(searchTimeout);
// Set new timeout
searchTimeout = setTimeout(() => {
console.log('Searching for:', query);
// Actual search logic here
}, 500); // Wait 500ms after user stops typing
}
search('hel');
search('hell');
search('hello'); // Only this search actually runs (after 500ms)setInterval() - Execute Repeatedly
setInterval() repeatedly executes a function at specified intervals until cleared.
Basic Syntax
// Syntax: setInterval(function, interval)
// interval is in milliseconds
let count = 0;
let intervalId = setInterval(function() {
count++;
console.log('Count:', count);
}, 1000); // Runs every 1 second
// Output:
// (after 1s) Count: 1
// (after 2s) Count: 2
// (after 3s) Count: 3
// ... continues forever until cleared
// Arrow function syntax
let intervalId2 = setInterval(() => {
console.log('Tick');
}, 2000); // Runs every 2 seconds
// Named function
function printTime() {
console.log(new Date().toLocaleTimeString());
}
let clockInterval = setInterval(printTime, 1000);
// Prints current time every secondPassing Arguments
// Method 1: Additional parameters
function countdown(seconds) {
console.log(`${seconds} seconds remaining`);
}
setInterval(countdown, 1000, 5);
// But this won't decrement - we need a different approach
// Method 2: Arrow function wrapper (better for counters)
let remaining = 10;
let countdownInterval = setInterval(() => {
console.log(`${remaining} seconds remaining`);
remaining--;
if (remaining < 0) {
clearInterval(countdownInterval);
console.log('Countdown complete!');
}
}, 1000);clearInterval() - Stop Repeating Execution
clearInterval() stops an interval from continuing to execute. Always clear intervals when done!
let count = 0;
let intervalId = setInterval(() => {
count++;
console.log('Count:', count);
// Stop after 5 iterations
if (count >= 5) {
clearInterval(intervalId);
console.log('Interval stopped');
}
}, 1000);
// Output:
// (1s) Count: 1
// (2s) Count: 2
// (3s) Count: 3
// (4s) Count: 4
// (5s) Count: 5
// Interval stopped
// Stop interval on button click
let ticker = setInterval(() => {
console.log('Tick');
}, 1000);
document.getElementById('stopBtn')?.addEventListener('click', () => {
clearInterval(ticker);
console.log('Ticker stopped');
});Memory Leak Warning: Always clear intervals when they're no longer needed! Forgotten intervals continue running forever, consuming memory and CPU.
setTimeout vs setInterval
Choose based on your needs
setTimeout (One-time)
// setTimeout - runs ONCE
let count = 0;
function incrementOnce() {
count++;
console.log('Count:', count);
}
setTimeout(incrementOnce, 1000);
setTimeout(incrementOnce, 2000);
setTimeout(incrementOnce, 3000);
// Each setTimeout schedules ONE execution
// Output:
// (1s) Count: 1
// (2s) Count: 2
// (3s) Count: 3
// (then stops)
// Recursive setTimeout for repetition
function repeat() {
console.log('Repeating');
setTimeout(repeat, 1000);
}
setTimeout(repeat, 1000);setInterval (Repeating)
// setInterval - runs REPEATEDLY
let count = 0;
function increment() {
count++;
console.log('Count:', count);
}
let intervalId = setInterval(increment, 1000);
// ONE setInterval schedules MULTIPLE executions
// Output:
// (1s) Count: 1
// (2s) Count: 2
// (3s) Count: 3
// (4s) Count: 4
// ... continues forever
// Must manually stop
setTimeout(() => {
clearInterval(intervalId);
}, 3500); // Stop after ~3.5 secondsPractical Patterns
Pattern 1: Countdown Timer
function startCountdown(seconds) {
let remaining = seconds;
// Show initial value
console.log(`${remaining} seconds`);
let intervalId = setInterval(() => {
remaining--;
console.log(`${remaining} seconds`);
if (remaining <= 0) {
clearInterval(intervalId);
console.log('Time is up!');
}
}, 1000);
// Return ID so caller can cancel if needed
return intervalId;
}
// Start 10-second countdown
let countdown = startCountdown(10);
// Cancel if needed
// clearInterval(countdown);Pattern 2: Digital Clock
function startClock() {
function updateClock() {
let now = new Date();
let hours = now.getHours().toString().padStart(2, '0');
let minutes = now.getMinutes().toString().padStart(2, '0');
let seconds = now.getSeconds().toString().padStart(2, '0');
let timeString = `${hours}:${minutes}:${seconds}`;
console.log(timeString);
// In real app: update DOM
// document.getElementById('clock').textContent = timeString;
}
// Show time immediately
updateClock();
// Update every second
return setInterval(updateClock, 1000);
}
let clockInterval = startClock();
// Stop clock
// clearInterval(clockInterval);Pattern 3: Auto-Save Feature
let content = '';
let hasUnsavedChanges = false;
function autoSave() {
if (hasUnsavedChanges) {
console.log('Auto-saving:', content);
// In real app: send to server
hasUnsavedChanges = false;
}
}
// Auto-save every 30 seconds
let autoSaveInterval = setInterval(autoSave, 30000);
// Simulate user typing
function onContentChange(newContent) {
content = newContent;
hasUnsavedChanges = true;
}
// Example usage
onContentChange('Hello world');
// After 30 seconds: "Auto-saving: Hello world"
// Stop auto-save when user leaves
window.addEventListener('beforeunload', () => {
clearInterval(autoSaveInterval);
if (hasUnsavedChanges) {
autoSave(); // Final save
}
});Pattern 4: Debouncing with setTimeout
// Debouncing: Wait until user stops typing before executing
function debounce(func, delay) {
let timeoutId;
return function(...args) {
// Clear previous timeout
clearTimeout(timeoutId);
// Set new timeout
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
// Search function (expensive operation)
function performSearch(query) {
console.log('Searching for:', query);
// Actual search logic here
}
// Debounced version (waits 500ms after last keystroke)
let debouncedSearch = debounce(performSearch, 500);
// Simulate user typing
debouncedSearch('h'); // Timeout set
debouncedSearch('he'); // Previous timeout cleared, new one set
debouncedSearch('hel'); // Previous timeout cleared, new one set
debouncedSearch('hell'); // Previous timeout cleared, new one set
debouncedSearch('hello'); // Previous timeout cleared, new one set
// After 500ms of no typing: "Searching for: hello"
// Only ONE search happens, after user stops typing!Pattern 5: Animation Loop
// Simple animation with setInterval
let position = 0;
function animate() {
position += 5; // Move 5 pixels
console.log(`Position: ${position}px`);
// In real app: update element position
// element.style.left = position + 'px';
// Stop at 500px
if (position >= 500) {
clearInterval(animationInterval);
console.log('Animation complete');
}
}
let animationInterval = setInterval(animate, 50); // ~20fps
// Note: For real animations, use requestAnimationFrame() instead
// It's optimized for animations and more efficientCommon Pitfalls and Solutions
Pitfall 1: Calling Function Immediately
function greet() {
console.log('Hello!');
}
// ✗ Wrong - calls greet() immediately, passes return value (undefined)
setTimeout(greet(), 1000); // "Hello!" logs immediately, not after 1s
// ✓ Correct - passes function reference
setTimeout(greet, 1000); // "Hello!" logs after 1s
// With arguments:
// ✗ Wrong
setTimeout(greet('Alice'), 1000); // Error or immediate execution
// ✓ Correct - use arrow function
setTimeout(() => greet('Alice'), 1000);Pitfall 2: Not Clearing Intervals
// ✗ Bad - interval never stops (memory leak)
function startBadClock() {
setInterval(() => {
console.log(new Date().toLocaleTimeString());
}, 1000);
}
startBadClock();
startBadClock(); // Now TWO clocks running!
// Each call creates a new interval that never stops
// ✓ Good - save ID and provide way to stop
function startGoodClock() {
let intervalId = setInterval(() => {
console.log(new Date().toLocaleTimeString());
}, 1000);
return intervalId; // Return so caller can stop it
}
let clock = startGoodClock();
// Later:
clearInterval(clock);Pitfall 3: 'this' Context Issues
let counter = {
count: 0,
// ✗ Bad - 'this' doesn't refer to counter
startBad: function() {
setInterval(function() {
this.count++; // 'this' is window/undefined
console.log(this.count); // NaN or error
}, 1000);
},
// ✓ Good - arrow function preserves 'this'
startGood: function() {
setInterval(() => {
this.count++; // 'this' is counter
console.log(this.count); // 1, 2, 3...
}, 1000);
},
// ✓ Also good - bind 'this'
startBind: function() {
setInterval(function() {
this.count++;
console.log(this.count);
}.bind(this), 1000);
}
};
counter.startGood();Pitfall 4: Overlapping Intervals
// ✗ Bad - if operation takes longer than interval
function badPoller() {
setInterval(() => {
// Expensive operation (might take > 1 second)
fetchDataFromServer(); // Takes 2 seconds
processData();
}, 1000); // Runs every 1 second
// Problem: Next interval starts before previous finishes!
// Multiple requests running simultaneously
}
// ✓ Good - use recursive setTimeout
function goodPoller() {
function poll() {
fetchDataFromServer()
.then(processData)
.then(() => {
// Only schedule next poll after this one completes
setTimeout(poll, 1000);
});
}
poll(); // Start first poll
}
// This ensures operations don't overlapPractical Examples
Example 1: Notification System
function showNotification(message, duration = 3000) {
console.log(`📢 ${message}`);
// In real app: show notification UI
// let notif = document.getElementById('notification');
// notif.textContent = message;
// notif.classList.add('show');
// Auto-hide after duration
setTimeout(() => {
console.log('Notification hidden');
// notif.classList.remove('show');
}, duration);
}
// Usage
showNotification('File saved successfully!');
showNotification('Welcome back, Alice!', 5000);Example 2: Pomodoro Timer
class PomodoroTimer {
constructor(workMinutes = 25, breakMinutes = 5) {
this.workDuration = workMinutes * 60;
this.breakDuration = breakMinutes * 60;
this.timeRemaining = this.workDuration;
this.isWorking = true;
this.intervalId = null;
}
start() {
if (this.intervalId) return; // Already running
this.intervalId = setInterval(() => {
this.timeRemaining--;
let minutes = Math.floor(this.timeRemaining / 60);
let seconds = this.timeRemaining % 60;
console.log(`${minutes}:${seconds.toString().padStart(2, '0')}`);
if (this.timeRemaining <= 0) {
this.switchMode();
}
}, 1000);
}
stop() {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
}
}
reset() {
this.stop();
this.timeRemaining = this.isWorking ? this.workDuration : this.breakDuration;
}
switchMode() {
this.isWorking = !this.isWorking;
this.timeRemaining = this.isWorking ? this.workDuration : this.breakDuration;
let mode = this.isWorking ? 'Work' : 'Break';
console.log(`
⏰ ${mode} time!
`);
}
}
// Usage
let pomodoro = new PomodoroTimer(25, 5);
pomodoro.start();
// Stop after some time
setTimeout(() => {
pomodoro.stop();
console.log('Timer stopped');
}, 10000);setTimeout and setInterval Practice
Master JavaScript timing functions
console.log() to see your output in the console above.Key Takeaways
setTimeout(fn, delay)executes once after delaysetInterval(fn, interval)executes repeatedly- Both return IDs that can be used to cancel them
clearTimeout(id)cancels a timeoutclearInterval(id)stops an interval- Delay/interval is in milliseconds (1000ms = 1 second)
- Don't call function with () - pass reference only
- Use arrow functions to preserve
thiscontext - Always clear intervals when done to prevent memory leaks
- Consider recursive setTimeout instead of setInterval for async operations
What's Next?
You now know how to schedule code execution with setTimeout and setInterval! These timing functions are building blocks for many async patterns, from simple delays to complex timers and polling mechanisms.
In the next lesson, we'll explore Callbacks and Callback Hell—learning how to handle async operations with callback functions and understanding the problems that arise when callbacks are nested deeply. This sets the stage for modern async solutions!
💪 Practice Challenge:
Before moving on, try creating:
- A countdown timer that displays remaining time every second
- A digital clock that updates in real-time
- A notification that auto-dismisses after 5 seconds
- A debounced search input that only searches after user stops typing
- A pomodoro timer with work/break cycles