JS Guide
HomeQuestionsTopicsCompaniesResources
BookmarksSearch

Built for developers preparing for JavaScript, React & TypeScript interviews.

ResourcesQuestionsSupport
HomeQuestionsSearchProgress
HomeQuestionsperformance
Prev

Learn the concept

Runtime Performance & Profiling

performance
senior
runtime

How do you identify and optimize long tasks on the main thread?

long-tasks
main-thread
performance
inp
web-workers
scheduling
Quick Answer

Long tasks are JavaScript executions exceeding 50ms that block the main thread, causing unresponsive UI. Identify them with the Long Tasks API and PerformanceObserver, then optimize by breaking work into smaller chunks, yielding to the main thread, and offloading CPU-intensive work to Web Workers.

Detailed Explanation

The browser's main thread handles JavaScript execution, layout, painting, and user input processing. When a single JavaScript task runs for more than 50ms, it's classified as a long task. During a long task, the browser cannot respond to user input (clicks, typing, scrolling), causing the UI to feel frozen.

Why 50ms?

To maintain a responsive UI, the browser needs to process a frame roughly every 16ms (60fps). Research shows users perceive delays above 100ms as sluggish. Keeping tasks under 50ms leaves headroom for rendering and input processing within that 100ms budget.

Identifying Long Tasks

1. Long Tasks API — Observe long tasks programmatically:

JavaScript
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('Long task detected:', {
      duration: entry.duration,
      startTime: entry.startTime,
      name: entry.name,
    });
  }
});
observer.observe({ type: 'longtask', buffered: true });

2. Chrome DevTools Performance panel — Record a trace and look for red triangles on the main thread timeline. Each marks a long task. Click to see the call stack.

3. Lighthouse / PageSpeed Insights — Reports Total Blocking Time (TBT), which sums all long task durations beyond 50ms. This correlates strongly with INP (Interaction to Next Paint).

Connection to INP

INP (Interaction to Next Paint) is a Core Web Vital measuring responsiveness. When a user interaction triggers JavaScript that becomes a long task, the time from interaction to visual feedback (paint) is delayed. Reducing long tasks directly improves INP.

Optimization Strategies

1. Break work into chunks — Split large loops or data processing into smaller batches:

JavaScript
async function processItems(items) {
  for (let i = 0; i < items.length; i += 100) {
    const chunk = items.slice(i, i + 100);
    processChunk(chunk);
    await yieldToMain(); // Give browser a chance to handle input
  }
}

2. Yield to the main thread — Use scheduler.yield() (experimental) or setTimeout(0) to let the browser process pending input between chunks.

3. Web Workers — Offload CPU-intensive work (parsing, sorting, image processing, encryption) to a background thread. Workers run in parallel and don't block the main thread.

4. Defer non-critical work — Use requestIdleCallback for analytics, prefetching, and other non-urgent tasks.

5. Code splitting — Lazy-load JavaScript modules so less code executes on initial page load. Use dynamic import() and React.lazy().

6. Reduce JavaScript execution — Use lighter libraries, remove unused code (tree shaking), and avoid unnecessary re-renders in React (memo, useMemo).

scheduler.yield() (Experimental)

The Scheduler API's yield() method pauses the current task, yields to the main thread for input processing, then resumes where it left off — maintaining task priority:

JavaScript
async function processData() {
  for (const item of largeArray) {
    doWork(item);
    await scheduler.yield(); // Browser handles input, then resumes
  }
}

This is superior to setTimeout(0) because it doesn't lose task priority and avoids the minimum 4ms delay.

Code Examples

Detecting and Reporting Long TasksJavaScript
// Monitor long tasks in production
function observeLongTasks() {
  if (!('PerformanceObserver' in window)) return;

  const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Report long tasks to analytics
      sendToAnalytics({
        type: 'long-task',
        duration: Math.round(entry.duration),
        startTime: Math.round(entry.startTime),
        // Attribution (which script caused it)
        attribution: entry.attribution?.[0]?.name || 'unknown',
      });

      // Log in development
      if (process.env.NODE_ENV === 'development') {
        console.warn(
          `Long task: ${Math.round(entry.duration)}ms`,
          entry.attribution
        );
      }
    }
  });

  observer.observe({ type: 'longtask', buffered: true });
}

observeLongTasks();

Real-World Applications

Use Cases

E-commerce Search and Filtering

Product filtering on pages with thousands of items can become a long task. Breaking the filter logic into chunks keeps the UI responsive while results update progressively.

Rich Text Editors

Syntax highlighting and spell-checking in text editors can block the main thread. Using Web Workers for parsing and requestIdleCallback for non-critical analysis keeps typing smooth.

Mini Projects

Long Task Dashboard

advanced

Build a performance monitoring dashboard that uses the Long Tasks API to detect, log, and visualize long tasks in real-time with their attribution and stack traces.

Industry Examples

Google

Google Search uses task chunking and scheduler.yield() to keep the search results page responsive even when rendering complex rich results and knowledge panels.

Resources

web.dev - Optimize Long Tasks

article

MDN - Long Tasks API

docs

web.dev - Interaction to Next Paint (INP)

article

Related Questions

What is requestIdleCallback and how does it compare to other scheduling APIs?

senior
scheduling

How do you identify and fix JavaScript runtime performance issues?

senior
runtime
Previous
How do you set up performance monitoring for production applications?
Prev