Apply a filter to a 4K image → frozen for 5 seconds.
Why: the for loop over 33 million values runs without giving the browser a chance to update the UI or respond to clicks.
Call Stack: [applyFilter → for loop (33M iterations)]
Task Queue: [click event, mousemove, repaint]
← WAITING (blocked!)
function applyFilterAsync(imageData, filterFn, onComplete) {
const pixels = imageData.data;
const chunkSize = 100000; // Process 100K pixels per chunk
let offset = 0;
function processChunk() {
const end = Math.min(offset + chunkSize * 4, pixels.length);
for (let i = offset; i < end; i += 4) {
const [newR, newG, newB] = filterFn(
pixels[i], pixels[i+1], pixels[i+2]
);
pixels[i] = clamp(newR);
pixels[i + 1] = clamp(newG);
pixels[i + 2] = clamp(newB);
}
offset = end;
if (offset < pixels.length) {
const progress = Math.round(
(offset / pixels.length) * 100
);
updateProgressBar(progress);
setTimeout(processChunk, 0); // Yield to browser
} else {
onComplete();
}
}
processChunk();
}
Show "Processing... 45%" with a visual progress bar that fills as chunks complete.
Test: UI stays responsive during processing! Buttons still work.
console.log("A");
setTimeout(() => console.log("B"), 0);
console.log("C");
// Output: A, C, B — why?
A runs immediately (call stack). setTimeout puts B in the task queue. C runs next (still on the call stack). Only after the stack is empty does the event loop pick B from the queue.
git switch -c perf/PIXELCRAFT-017-async-processing
git add src/scripts/ src/styles/
git commit -m "Async chunk processing with progress bar (PIXELCRAFT-017)"
git push origin perf/PIXELCRAFT-017-async-processing
# PR → Review → Merge → Close ticket ✅
The event loop is JavaScript's concurrency model.
Unlike languages with threads (Java, C++), JS uses a single thread with cooperative multitasking.