Heavy Background Processing
Strategies for preventing main thread lockups when performing heavy calculations, parsing massive JSON, or filtering large datasets on the client.
The Problem
JavaScript is single-threaded by default. If a function takes 500ms to calculate a physics simulation, filter a 100,000-row array, or parse a 50MB JSON payload, the entire browser tab freezes for 500ms. Clicks, scrolls, and animations completely stop.
Common Symptoms
- GIFs and CSS animations abruptly freeze.
- Typing in inputs feels laggy or ignores keystrokes.
- The browser displays the "Page Unresponsive" dialog.
- Lighthouse reports high "Total Blocking Time" (TBT).
Root Causes
- Long tasks (any execution taking > 50ms) block the Event Loop.
- Processing large arrays synchronously.
- Complex cryptography, image processing, or data aggregation running on the main thread.
Typical Expected Improvements
Decision Matrix
Architectural decision guidance for different scenarios.
Scenario: You need to filter a massive dataset (e.g. 50,000 objects) without freezing the UI.
⚖️ Trade-offs: Requires transferring data via postMessage, which has serialization costs. You cannot access the DOM.
💡 Why: Web Workers run in a completely separate OS thread. The main thread remains at 0% CPU utilization during the filter, ensuring perfect 60fps scrolling.
Scenario: You need to render a complex component tree progressively, but you cannot use a Worker because it relies on the DOM.
⚖️ Trade-offs: The overall processing time will be slightly longer due to context switching, but the UI remains responsive.
💡 Why: By chunking the work into small batches using `setTimeout` or `requestIdleCallback`, the browser can process user inputs between chunks.
- If the calculation takes less than 10-20ms. The overhead of spinning up a worker or yielding might be slower than just running it.
- If the task requires direct, synchronous access to the DOM (`document` or `window`).
Approaches
Do This
- Offload pure computational tasks to Web Workers.
- Use `Transferable Objects` (like ArrayBuffers) when passing data to Workers to achieve zero-copy speed.
- Yield to the main thread manually using `setTimeout(0)` or `scheduler.yield()` for chunkable DOM tasks.
Anti-patterns
- Processing 50MB JSON strings synchronously using `JSON.parse` on the main thread.
- Using heavy `reduce` or `filter` chains on massive arrays directly inside a UI component render function.
- Sending massive deeply-nested objects back and forth to a Worker (serialization bottleneck).
Implementation
import { useVirtualList } from '@vueuse/core' const { list, containerProps, wrapperProps } = useVirtualList( hugeDataArray, { itemHeight: 48 } )
Use AI
Instantly analyze your codebase using this recipe via MCP. Open your AI agent and run this prompt.