Blocking the Main Thread in JavaScript
JavaScript has one thread. Blocking it freezes the UI. Here is why and how to avoid it.
Blocking the Main Thread in JavaScript
JavaScript has a single call stack (one thread). When a function is running, nothing else can run. If that function takes a long time, the UI freezes. This is called blocking the main thread.
What Blocking Looks Like
function heavy() { for (let i = 0; i < 1e9; i++) {} // 5 seconds } heavy(); // during these 5 seconds: clicks, scrolls, animations, timers all freeze `` The browser cannot paint, respond to clicks, or run queued callbacks until `heavy` returns. ### What Blocks the Main Thread - Long loops (`for`, `while`). - Heavy computation (parsing large JSON, sorting huge arrays). - Synchronous APIs (`JSON.parse` of huge strings, `crypto.subtle` in some cases). - Deep recursion. - Infinite loops (the page hangs forever). ### What Does NOT Block - `setTimeout`, `setInterval`: the timer runs in a Web API, only the callback runs on the main thread. - `fetch`, `XMLHttpRequest`: the network call runs in a Web API. - Event listeners: they wait for the browser to dispatch the event. - Promises: the async work runs outside the engine; only the `.then` callback runs on the main thread. ### How to Avoid Blocking #### 1. Break Long Tasks Into Chunks ```js function chunked(items, chunkSize = 100) { let i = 0; function next() { const end = Math.min(i + chunkSize, items.length); for (; i < end; i++) { process(items[i]); } if (i < items.length) setTimeout(next, 0); } next(); } `` Process 100 items, then yield via `setTimeout(cb, 0)`. The browser can paint and respond between chunks. #### 2. Use Web Workers ```js const worker = new Worker("heavy.js"); worker.postMessage(data); worker.onmessage = (event) => console.log(event.data); `` Web Workers run JS on a separate thread. The main thread is not blocked. Use for heavy computation. #### 3. Use Async APIs Prefer `fetch` over `XMLHttpRequest` (sync), async `fs.promises` over sync `fs` in Node.js, and `crypto.subtle` over sync crypto. #### 4. Debounce and Throttle ```js const onScroll = throttle(() => updateLayout(), 100); window.addEventListener("scroll", onScroll); `` Limit how often expensive handlers run. #### 5. Use `requestAnimationFrame` for Visual Updates ```js function animate() { // update the DOM requestAnimationFrame(animate); } requestAnimationFrame(animate); `` `requestAnimationFrame` runs the callback before the next paint, keeping animations smooth. #### 6. Use `requestIdleCallback` for Low-Priority Work ```js requestIdleCallback(() => { // runs when the browser is idle doLowPriorityWork(); }); `` ### Detecting Main Thread Blocking - **Chrome DevTools Performance tab**: record and look for long tasks (red flags). - **`PerformanceObserver`**: observe long tasks programmatically. - **Long Tasks API**: `PerformanceObserver({ entryTypes: ["longtask"] })`. ### The Takeaway Blocking the main thread freezes the UI. Avoid it by breaking long tasks into chunks (with `setTimeout`), using Web Workers for heavy computation, preferring async APIs, debouncing/throttling expensive handlers, using `requestAnimationFrame` for visual updates, and `requestIdleCallback` for low-priority work.
Running a long synchronous function that occupies the single call stack. While it runs, the browser cannot paint, respond to clicks, or run queued callbacks. The UI freezes.
Break long tasks into chunks with setTimeout, use Web Workers for heavy computation, prefer async APIs, debounce and throttle expensive handlers, use requestAnimationFrame for visual updates, and requestIdleCallback for low-priority work.
No. The timer runs in a Web API outside the engine. Only the callback runs on the main thread, and only when the stack is empty. The scheduling does not block.
Web Workers run JavaScript on a separate thread. Heavy computation can be sent to a worker, and the main thread stays responsive. The worker posts results back via messages.
Use Chrome DevTools Performance tab (record and look for long tasks with red flags). Use the Long Tasks API: PerformanceObserver with entryTypes ['longtask']. Long tasks are anything over 50ms.
Ready to master React completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master React
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

