Event Loop & Call Stack in JavaScript - Exercise 3
It occurs when code continually adds microtasks, preventing the event loop from reaching regular tasks such as timers.
function keepBusy() {
queueMicrotask(keepBusy);
}
// keepBusy(); // starves the task queueA long-running function occupies the call stack, so timers, input, rendering, and network callbacks must wait.
function blockFor(milliseconds) {
const start = Date.now();
while (Date.now() - start < milliseconds) {}
}
blockFor(200);Process a small batch and schedule the next batch as a task so other work can run between batches.
function processBatch(items, index = 0) {
const nextIndex = Math.min(index + 100, items.length);
for (let i = index; i < nextIndex; i += 1) process(items[i]);
if (nextIndex < items.length) setTimeout(() => processBatch(items, nextIndex), 0);
}Promise handlers are microtasks, which are drained before the event loop takes the next task from the callback queue.
button.addEventListener('click', () => console.log('click task'));
Promise.resolve().then(() => console.log('promise microtask'));
// The Promise handler runs before a later click task.Tasks include timers and DOM events; microtasks include Promise reactions and queueMicrotask callbacks. Microtasks run between tasks.
setTimeout(() => console.log('task'), 0);
queueMicrotask(() => console.log('microtask'));
// microtask runs before taskIt schedules visual updates before the browser's next repaint, making it suitable for animation rather than arbitrary background work.
let position = 0;
function animate() {
position += 1;
box.style.transform = `translateX(${position}px)`;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);Use bounded batches, yield with a task scheduler, and avoid unbounded synchronous loops or microtask chains.
async function processAll(items) {
for (const item of items) {
process(item);
await new Promise(resolve => setTimeout(resolve, 0));
}
}Label synchronous calls, task scheduling, microtask scheduling, and each callback, then trace when the stack empties and which queue has priority.
console.log('A');
setTimeout(() => console.log('D'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('B');
// A, B, C, D