Async Events
What are Async Events?
Asynchronous events allow JavaScript to perform non-blocking operations. The event loop manages the execution of async code, enabling responsive applications.
Try Async Events
Code Editor
Console Output
Click "Run" to execute your code...
Example 1: Event Loop Basics
// JavaScript Event Loop
console.log('1: Start');
setTimeout(() => {
console.log('2: Timeout');
}, 0);
Promise.resolve().then(() => {
console.log('3: Promise');
});
console.log('4: End');
// Output:
// 1: Start
// 4: End
// 3: Promise
// 2: Timeout
// Explanation:
// 1. Synchronous code runs first (1, 4)
// 2. Microtasks (Promises) run next (3)
// 3. Macrotasks (setTimeout) run last (2)
// Call Stack -> Microtask Queue -> Macrotask Queue
// Detailed example
console.log('Script start');
setTimeout(() => {
console.log('setTimeout 1');
}, 0);
Promise.resolve()
.then(() => {
console.log('Promise 1');
return Promise.resolve();
})
.then(() => {
console.log('Promise 2');
});
setTimeout(() => {
console.log('setTimeout 2');
Promise.resolve().then(() => {
console.log('Promise 3');
});
}, 0);
console.log('Script end');
// Output:
// Script start
// Script end
// Promise 1
// Promise 2
// setTimeout 1
// setTimeout 2
// Promise 3Example 2: Async/Await with Events
// Async function with events
async function fetchData() {
console.log('Fetching data...');
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
}
// Using async/await
async function main() {
try {
const data = await fetchData();
console.log('Data received:', data);
} catch (error) {
console.error('Error:', error);
}
}
main();
// Event listener with async handler
button.addEventListener('click', async (event) => {
console.log('Button clicked');
try {
const result = await someAsyncOperation();
console.log('Result:', result);
} catch (error) {
console.error('Error:', error);
}
});
// Multiple async operations
async function processMultiple() {
// Sequential (one after another)
const result1 = await operation1();
const result2 = await operation2();
const result3 = await operation3();
console.log(result1, result2, result3);
}
// Parallel (all at once)
async function processParallel() {
const [result1, result2, result3] = await Promise.all([
operation1(),
operation2(),
operation3()
]);
console.log(result1, result2, result3);
}
// Race condition
async function firstToFinish() {
const result = await Promise.race([
operation1(),
operation2(),
operation3()
]);
console.log('First result:', result);
}Example 3: setTimeout and setInterval
// setTimeout - executes once after delay
const timeoutId = setTimeout(() => {
console.log('Executed after 2 seconds');
}, 2000);
// Clear timeout
clearTimeout(timeoutId);
// setTimeout with arguments
setTimeout((name, age) => {
console.log(`${name} is ${age} years old`);
}, 1000, 'John', 30);
// setInterval - executes repeatedly
const intervalId = setInterval(() => {
console.log('Executed every second');
}, 1000);
// Clear interval
clearInterval(intervalId);
// Auto-clearing interval
let count = 0;
const countInterval = setInterval(() => {
count++;
console.log(`Count: ${count}`);
if (count === 5) {
clearInterval(countInterval);
console.log('Stopped!');
}
}, 1000);
// Recursive setTimeout (better than setInterval)
function repeatTask() {
console.log('Task executed');
setTimeout(repeatTask, 1000);
}
setTimeout(repeatTask, 1000);
// Why recursive setTimeout is better:
// - Guarantees delay between executions
// - Can adjust delay dynamically
// - Won't queue up if execution takes longer than interval
// Debounce with setTimeout
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
// Usage: Search as user types
const searchInput = document.getElementById('search');
const debouncedSearch = debounce((value) => {
console.log('Searching for:', value);
// API call here
}, 500);
searchInput.addEventListener('input', (e) => {
debouncedSearch(e.target.value);
});
// Throttle with setTimeout
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// Usage: Scroll event
const throttledScroll = throttle(() => {
console.log('Scroll position:', window.scrollY);
}, 200);
window.addEventListener('scroll', throttledScroll);Example 4: Request Animation Frame
// requestAnimationFrame - synced with browser refresh (60fps)
function animate() {
// Update animation
console.log('Frame updated');
// Request next frame
requestAnimationFrame(animate);
}
// Start animation
const animationId = requestAnimationFrame(animate);
// Stop animation
cancelAnimationFrame(animationId);
// Smooth animation example
const box = document.getElementById('box');
let position = 0;
function moveBox() {
position += 2;
box.style.left = position + 'px';
if (position < 500) {
requestAnimationFrame(moveBox);
}
}
requestAnimationFrame(moveBox);
// Performance monitoring
let lastTime = performance.now();
let frames = 0;
function measureFPS() {
frames++;
const currentTime = performance.now();
const elapsed = currentTime - lastTime;
if (elapsed >= 1000) {
const fps = Math.round(frames / (elapsed / 1000));
console.log(`FPS: ${fps}`);
frames = 0;
lastTime = currentTime;
}
requestAnimationFrame(measureFPS);
}
requestAnimationFrame(measureFPS);
// Smooth scroll animation
function smoothScrollTo(targetY, duration) {
const startY = window.scrollY;
const distance = targetY - startY;
const startTime = performance.now();
function scroll(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Easing function
const ease = progress < 0.5
? 2 * progress * progress
: 1 - Math.pow(-2 * progress + 2, 2) / 2;
window.scrollTo(0, startY + distance * ease);
if (progress < 1) {
requestAnimationFrame(scroll);
}
}
requestAnimationFrame(scroll);
}
// Usage
smoothScrollTo(1000, 1000); // Scroll to 1000px in 1 secondExample 5: Async Patterns
// Pattern 1: Loading states
async function loadData() {
const loadingEl = document.getElementById('loading');
const contentEl = document.getElementById('content');
const errorEl = document.getElementById('error');
try {
loadingEl.style.display = 'block';
errorEl.style.display = 'none';
const data = await fetch('/api/data').then(r => r.json());
contentEl.innerHTML = renderData(data);
contentEl.style.display = 'block';
} catch (error) {
errorEl.textContent = error.message;
errorEl.style.display = 'block';
} finally {
loadingEl.style.display = 'none';
}
}
// Pattern 2: Retry logic
async function fetchWithRetry(url, retries = 3, delay = 1000) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
if (response.ok) {
return await response.json();
}
} catch (error) {
if (i === retries - 1) throw error;
console.log(`Retry ${i + 1}/${retries} after ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
delay *= 2; // Exponential backoff
}
}
}
// Pattern 3: Timeout for async operations
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Timeout')), ms);
});
return Promise.race([promise, timeout]);
}
// Usage
try {
const data = await withTimeout(
fetch('/api/slow-endpoint'),
5000
);
} catch (error) {
console.error('Request timed out or failed');
}
// Pattern 4: Queue for async tasks
class AsyncQueue {
constructor(concurrency = 1) {
this.concurrency = concurrency;
this.running = 0;
this.queue = [];
}
async add(task) {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject });
this.run();
});
}
async run() {
if (this.running >= this.concurrency || this.queue.length === 0) {
return;
}
this.running++;
const { task, resolve, reject } = this.queue.shift();
try {
const result = await task();
resolve(result);
} catch (error) {
reject(error);
} finally {
this.running--;
this.run();
}
}
}
// Usage
const queue = new AsyncQueue(2); // Max 2 concurrent tasks
for (let i = 0; i < 10; i++) {
queue.add(async () => {
console.log(`Task ${i} started`);
await new Promise(r => setTimeout(r, 1000));
console.log(`Task ${i} completed`);
return i;
});
}Key Points
- Event loop manages asynchronous code execution
- Microtasks (Promises) execute before macrotasks (setTimeout)
- async/await makes asynchronous code look synchronous
- Use requestAnimationFrame for smooth animations
- Debounce delays execution until after events stop
- Throttle limits execution frequency
- Always handle errors in async code with try/catch