Mahamudul Hasan Rubel
HomeBlogCoursesAboutProjectsSkillsExperiencePhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • Blog
  • Courses
  • About
  • Projects
  • Skills
  • Experience
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

Subscribe to the newsletter

Get new articles and course lessons delivered to your inbox. No spam, unsubscribe anytime.

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
PerformanceJune 26, 20264 min read

Web Performance: Preventing Main-Thread Congestion with Workers

Web Performance starts with a responsive main thread. Learn how to use Web Workers and MessageChannel to offload heavy tasks and improve your Core Web Vitals.

Web PerformanceJavaScriptWeb WorkersCore Web VitalsOptimizationPerformanceWeb VitalsFrontend

Last month, I spent about three days chasing a stuttering dashboard. Every time a user triggered a complex data transformation, the entire interface froze for roughly 400ms—well above the threshold for a smooth interaction. It was a classic case of main-thread starvation, and my standard attempts to use requestIdleCallback weren't cutting it.

If you’ve been struggling with your INP (Interaction to Next Paint) scores, you know that the browser's main thread is a bottleneck. When you run heavy JavaScript, the rendering pipeline stops. To fix this, you need a strategy for Web Performance that treats the main thread as a luxury resource.

Understanding the Main Thread Bottleneck

The browser event loop is simple but unforgiving. It handles user input, layout, paint, and your scripts. If you dump a massive JSON parsing task or a complex data filter onto it, the event loop waits for that task to finish before it can process anything else.

We initially tried breaking the work into chunks using setTimeout, but that just spread the pain out without actually reducing the total execution time. It made the UI slightly less "dead," but the total blocking time remained unchanged. We needed to move the execution context entirely.

Leveraging Web Workers for Off-thread Processing

Web Workers provide a way to run scripts in background threads. By moving your heavy lifting here, you free up the main thread to focus on what it does best: rendering frames and responding to user clicks.

Here is how we set up a simple worker to handle our data transformation:

JAVASCRIPT
// main.js
const worker = new Worker(CE9178">'processor.js');

worker.postMessage({ data: largeDataset });

worker.onmessage = (event) => {
  console.log(CE9178">'Processed data:', event.data);
};

This is the baseline for Main Thread Optimization. However, workers are isolated. You can't just reach into the DOM or access global variables. You have to pass data back and forth, which can sometimes introduce serialization overhead if you aren't careful.

Advanced Communication with MessageChannel

Sometimes, you need more than a one-off message. If you’re building a complex application, you might need a direct, persistent pipe between your main thread and a worker. That’s where MessageChannel shines.

Instead of relying on the standard postMessage interface, MessageChannel creates two ports. You can transfer one port to the worker, allowing for direct communication that doesn't clutter the main worker listener.

JAVASCRIPT
// main.js
const channel = new MessageChannel();
const worker = new Worker(CE9178">'worker.js');

// Send one port to the worker
worker.postMessage({ type: CE9178">'INIT_PORT' }, [channel.port2]);

// Use the other port for direct communication
channel.port1.onmessage = (event) => {
  console.log(CE9178">'Direct response:', event.data);
};

This pattern is cleaner when you’re managing multiple background tasks. It’s also a great way to coordinate complex states without flooding the global event listener. If you are still seeing UI hitches, INP optimization: How to master the browser event loop covers how to detect if your tasks are still too long.

When Workers Aren't Enough

Sometimes, the bottleneck isn't your code—it's the third-party scripts you’re forced to run. If your analytics or ad tags are blocking the thread, standard workers won't help because those scripts need DOM access. In those cases, Third-Party Script Optimization: Offloading Scripts with Partytown is usually the right path.

I also learned the hard way that moving work to a worker doesn't mean you can ignore JavaScript Execution efficiency. If you pass a massive object via postMessage, the browser has to serialize it using the Structured Clone Algorithm. For massive datasets, this serialization itself can block the main thread for several milliseconds.

If you're dealing with massive buffers, consider using Transferable objects (like ArrayBuffer) to move memory ownership instead of copying data. It’s a game-changer for speed.

Staying Responsive

After moving our data processing to a dedicated worker and switching to MessageChannel for communication, our long-task count dropped significantly. The dashboard felt snappy again, and our Core Web Vitals metrics stabilized.

However, I’m still cautious. Web Workers aren't a silver bullet. They introduce complexity, especially around state synchronization and debugging. If I were to do this again, I’d invest more time in measuring the serialization cost before assuming a worker will solve everything.

Don't just throw code into a worker to hide it. Profile first. Use the Chrome DevTools "Performance" tab to see exactly what's blocking the thread, and only offload what actually needs to move.

Frequently Asked Questions

1. Does using Web Workers always improve performance? Not always. The overhead of serializing data between threads can sometimes be slower than running the task on the main thread if the data is small.

2. Can Web Workers access the DOM? No, they run in a separate global scope (DedicatedWorkerGlobalScope) and don't have access to the window or document objects.

3. What is the best way to debug worker-related issues? The "Sources" tab in Chrome DevTools is your best friend. You can select the worker thread from the thread dropdown to pause execution and inspect variables inside the worker itself.

Back to Blog

Similar Posts

PerformanceJune 27, 20264 min read

Adaptive Loading: Mastering Client Hints and Network Information API

Adaptive Loading using Client Hints and the Network Information API helps you deliver faster experiences. Learn how to tailor assets to real user conditions.

Read more
PerformanceJune 28, 20264 min read

Bundle size optimization: Auditing Dependency Graphs for Faster Loads

Master bundle size optimization by auditing your dependency graph. Learn to strip unused code, use subpath imports, and enforce performance budgets today.

Read more
PerformanceJune 27, 20264 min read

Web Workers for JSON Parsing: Stop Blocking the Main Thread

Web Workers for JSON parsing can drastically improve your app's performance. Learn how to offload data serialization to keep your main thread responsive.

Read more