Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
requestIdleCallback runs your code when the browser has free time (idle periods). Perfect for non-urgent tasks like analytics, prefetching, or background processing without blocking the UI!
Runs when browser isn't busy
Non-urgent background tasks
Won't block user interactions
See how requestIdleCallback runs tasks during idle time
<div style="max-width: 700px; margin: 0 auto; font-family: 'Segoe UI', sans-serif;">
<div style="background: linear-gradient(135deg, #f97316 0%, #f59e0b 50%, #eab308 100%); padding: 40px; border-radius: 16px; box-shadow: 0 10px 30px rgba(249, 115, 22, 0.3);">
<h2 style="color: white; margin: 0 0 12px 0; font-size: 28px; font-weight: 700; text-align: center;">☕ Idle Callback Demo</h2>
<p style="color: rgba(255, 255, 255, 0.9); margin: 0 0 30px 0; font-size: 15px; text-align: center;">Schedule tasks to run during browser idle time</p>
<div style="background: rgba(255, 255, 255, 0.95); padding: 24px; border-radius: 12px; backdrop-filter: blur(10px);">
<!-- Task Buttons -->
<div style="display: grid; gap: 12px; margin-bottom: 20px;">
<button id="urgentBtn" style="padding: 14px; background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 15px;">
🔥 Urgent Task (Immediate)
</button>
<button id="idleBtn" style="padding: 14px; background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 15px;">
☕ Idle Task (When Free)
</button>
<button id="batchBtn" style="padding: 14px; background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 15px;">
📦 Schedule 10 Idle Tasks
</button>
</div>
<!-- Status Display -->
<div id="status" style="padding: 16px; background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); border-radius: 8px; border-left: 4px solid #f59e0b; min-height: 120px; margin-bottom: 16px;"></div>
<!-- Task Queue -->
<div id="queue" style="font-size: 12px; color: #6b7280;"></div>
</div>
</div>
</div>Loading preview...
// Schedule a task to run during idle time
requestIdleCallback((deadline) => {
// Check how much time you have
console.log('Time remaining:', deadline.timeRemaining(), 'ms');
// Do low-priority work
processAnalytics();
prefetchData();
cleanupCache();
// Check if we ran out of time
if (deadline.didTimeout) {
console.log('Task was forced to run (timeout reached)');
}
});
// With timeout option (max wait time)
requestIdleCallback((deadline) => {
doBackgroundWork();
}, { timeout: 2000 }); // Run within 2 seconds even if not idle
// Cancel if needed
const callbackId = requestIdleCallback(myTask);
cancelIdleCallback(callbackId);// Process 10,000 items without blocking UI
const items = Array.from({ length: 10000 }, (_, i) => i);
let currentIndex = 0;
function processChunk(deadline) {
// Process items while we have time (or at least 1ms)
while (deadline.timeRemaining() > 1 && currentIndex < items.length) {
// Process one item
const item = items[currentIndex];
processItem(item);
currentIndex++;
}
// If more items remain, schedule another chunk
if (currentIndex < items.length) {
console.log(`Processed ${currentIndex}/${items.length}`);
requestIdleCallback(processChunk);
} else {
console.log('✅ All items processed!');
}
}
// Start processing
requestIdleCallback(processChunk);
// 🎯 Benefits:
// - UI stays responsive
// - Work happens in small chunks
// - Browser decides when to run
// - No janky scrolling or interactions// Queue for low-priority tasks
const taskQueue = [];
function addTask(task) {
taskQueue.push(task);
// Schedule processing if not already scheduled
if (!isProcessing) {
scheduleTaskProcessing();
}
}
let isProcessing = false;
function scheduleTaskProcessing() {
isProcessing = true;
requestIdleCallback((deadline) => {
// Process tasks while we have time
while (deadline.timeRemaining() > 0 && taskQueue.length > 0) {
const task = taskQueue.shift();
task();
}
// If more tasks remain, schedule another round
if (taskQueue.length > 0) {
scheduleTaskProcessing();
} else {
isProcessing = false;
}
});
}
// Usage
addTask(() => sendAnalytics());
addTask(() => prefetchImage('/img.jpg'));
addTask(() => cacheData());
// All tasks run during idle time!// Send analytics during idle time
function trackEvent(event) {
requestIdleCallback(() => {
fetch('/api/analytics', {
method: 'POST',
body: JSON.stringify(event)
});
});
}
// User clicks button - immediate UI response
button.addEventListener('click', () => {
// Update UI immediately
button.textContent = 'Clicked!';
// Track analytics during idle time (no blocking)
trackEvent({ action: 'button_click', timestamp: Date.now() });
});
// 🎯 UI updates instantly, tracking happens later// Prefetch next pages during idle time
const pagesToPrefetch = ['/page2', '/page3', '/page4'];
function prefetchPages() {
requestIdleCallback((deadline) => {
while (deadline.timeRemaining() > 0 && pagesToPrefetch.length > 0) {
const page = pagesToPrefetch.shift();
// Prefetch the page
fetch(page)
.then(response => response.text())
.then(html => {
// Cache it
cache.put(page, html);
console.log(`Prefetched: ${page}`);
});
}
// More pages? Schedule another round
if (pagesToPrefetch.length > 0) {
prefetchPages();
}
});
}
// Start prefetching when page loads
window.addEventListener('load', prefetchPages);// Clean up old cache entries during idle time
function cleanupCache() {
requestIdleCallback((deadline) => {
const cache = getCacheEntries();
const now = Date.now();
const maxAge = 24 * 60 * 60 * 1000; // 24 hours
for (const [key, entry] of cache.entries()) {
// Stop if we're out of time
if (deadline.timeRemaining() < 1) {
// Schedule continuation
requestIdleCallback(cleanupCache);
break;
}
// Remove old entries
if (now - entry.timestamp > maxAge) {
cache.delete(key);
console.log(`Removed old cache: ${key}`);
}
}
});
}
// Run cleanup periodically
setInterval(cleanupCache, 5 * 60 * 1000); // Every 5 minutes// Load below-the-fold images during idle time
function lazyLoadImages() {
const images = document.querySelectorAll('img[data-src]');
requestIdleCallback((deadline) => {
for (const img of images) {
// Stop if out of time
if (deadline.timeRemaining() < 10) {
// Continue later
if (images.length > 0) {
requestIdleCallback(lazyLoadImages);
}
break;
}
// Load the image
img.src = img.dataset.src;
img.removeAttribute('data-src');
console.log('Loaded image during idle time');
}
});
}
// Start lazy loading after page load
window.addEventListener('load', () => {
setTimeout(lazyLoadImages, 1000); // After initial render
});Chrome 47+, Edge 79+, Firefox 55+. Not supported in Safari (use polyfill or fallback to setTimeout)
// Polyfill for Safari
if (!window.requestIdleCallback) {
window.requestIdleCallback = function(cb) {
return setTimeout(cb, 1);
};
}Always check deadline.timeRemaining() and break work into small chunks. Long tasks should be split across multiple idle callbacks!
Send tracking events without blocking user interactions
Load next pages, images, or data in the background
Remove old cache, expired data, or temporary files
Process queued operations when browser is free
Runs when browser has free time
Non-urgent background tasks only
Check available time in idle period
Keeps interface smooth and responsive