Heavy Background Processing

Strategies for preventing main thread lockups when performing heavy calculations, parsing massive JSON, or filtering large datasets on the client.

Advanced
1-2 days
Impact: High
Related
Browser APIs:web-workers
Experiments:concurrency

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

DOM Nodes
↓ 95%
Memory
↓ 80%
FPS
↑ 3–5×
Initial Render
↓ 70%

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.

Recommended:Web Workers
🔄 Alternatives:Time Slicing (setTimeout)

⚖️ 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.

Recommended:Time Slicing (Yielding to Main Thread)
🔄 Alternatives:Web Workers

⚖️ 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.

When NOT To Use
  • 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.

Review my Vue table for virtualization opportunitiesCopy Prompt