How the Long Tasks API Monitors Main-Thread Stalls

This article explains how the Long Tasks API enables JavaScript applications to detect and diagnose main-thread execution stalls. By leveraging the browser’s performance timeline infrastructure, the API automatically flags any uninterrupted task exceeding 50 milliseconds and delivers detailed diagnostic records to the application asynchronously via the PerformanceObserver interface.

The 50-Millisecond Threshold

JavaScript runs in a single-threaded execution environment within the browser’s event loop. When a script, layout calculation, or rendering process occupies the main thread for too long, user inputs, animations, and page interactions freeze.

The Long Tasks API defines a “long task” as any contiguous, synchronous execution block that takes 50 milliseconds or longer to complete. This boundary is based on the RAIL performance model, which recommends responding to user input within 100 milliseconds. Reserving a 50-millisecond window allows the browser up to 50 milliseconds to finish ongoing tasks and another 50 milliseconds to process the incoming user event without perceivable latency.

The PerformanceObserver Notification Mechanism

Because a stalled main thread cannot execute monitoring code during the freeze itself, the Long Tasks API relies on passive, asynchronous reporting. It does not interrupt or poll the running thread. Instead, the browser’s internal engine monitors task execution times and logs entries to the Performance Timeline.

Developers access these notifications by registering a PerformanceObserver configured to watch for the longtask entry type.

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.warn(`Long task detected: ${entry.duration}ms`);
    console.log(`Started at: ${entry.startTime}ms`);
    console.log('Attribution:', entry.attribution);
  }
});

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

The buffered: true flag ensures that any long tasks that occurred prior to observer initialization (such as during initial page load and script parsing) are retrieved and delivered to the callback.

Asynchronous Delivery via the Event Loop

When a long task executes, the following sequence occurs:

  1. Detection: The browser engine measures the duration of a discrete task in the event loop. If the task duration reaches or exceeds 50 milliseconds, the browser creates a PerformanceLongTaskTiming entry.
  2. Buffering: The entry is stored in the browser’s internal performance timeline buffer.
  3. Queueing the Callback: Once the main thread becomes idle, the browser queues a microtask or task to execute the registered PerformanceObserver callback.
  4. Data Delivery: The observer callback receives a list of entries, passing the execution details directly to the application without blocking ongoing interactions.

Structure of a Long Task Notification

Each reported PerformanceLongTaskTiming object contains critical diagnostic metrics:

Application Use Cases

JavaScript applications use the Long Tasks API to power Real User Monitoring (RUM) pipelines. By aggregating long task durations, teams can calculate Total Blocking Time (TBT), identify expensive third-party scripts, and isolate components causing input delay to optimize Core Web Vitals such as Interaction to Next Paint (INP).