HomeMethod

Knowledge

Web VitalsTechniquesSnippetsAnswersImpactsBottlenecks

Break Up Long Tasks

Split JavaScript tasks longer than 50ms into smaller chunks so the browser stays responsive.

Overview

The browser’s main thread processes JavaScript, handles user input, performs layout, and paints to screen — all on a single thread. Any JavaScript task longer than 50 ms is a long task that blocks the thread from responding to user input for its entire duration. Total Blocking Time is the sum of all time beyond 50 ms across all long tasks during page load. Breaking long tasks into smaller yielding chunks eliminates TBT and dramatically improves Interaction to Next Paint.

Impact grade, A+ to D: how much of the Lighthouse score this technique can move. Unaffected: LCP, CLS, FCP, TTFB.

The Problem

On a typical WordPress page with a page builder, analytics, chat widget, and slider, the main thread can be blocked for 2–5 seconds during load by a cascade of long tasks. Each 200 ms task is one 150 ms contribution to TBT. A user clicking a button during this window waits the full remaining task duration before getting a response — the defining cause of poor INP scores. Lighthouse surfaces this as ‘Minimize main-thread work’ and ‘Reduce JavaScript execution time’.

Performance Impact

TBT

100–600ms

long tasks become many sub-50ms tasks; each contributes 0ms to TBT

SI

50–200ms

yielding between tasks allows paint frames to execute sooner

Risk: Low

logic is unchanged — only the scheduling is modified

How it works

  1. 01

    Identify long tasks with DevTools

    Open Chrome DevTools → Performance. Record a page load and look for red-flagged tasks in the Main thread lane. Any bar longer than 50 ms with a red triangle is a long task. Lighthouse also lists them under ‘Avoid long main-thread tasks’.

  2. 02

    Yield with scheduler.yield()

    await scheduler.yield() (Chrome 115+) pauses the current task and re-queues the remainder with high priority, giving the browser a frame to handle input before continuing. It is the lowest-latency yield mechanism available.

  3. 03

    Use setTimeout(fn, 0) as a universal fallback

    setTimeout(callback, 0) defers execution to a new task, giving the browser a chance to process input between chunks. Wrap large loops or sequential processing steps in setTimeout calls to break them across multiple tasks.

  4. 04

    Use requestIdleCallback for non-critical work

    requestIdleCallback runs work only during browser idle periods — analytics pings, prefetching, reporting. It never runs during active rendering or input handling, making it the safest way to defer work that does not need to complete by a specific time.

Limitations

  • Yielding introduces asynchrony — code that previously ran synchronously now requires async/await, which changes error handling and execution order.
  • scheduler.yield() is Chrome 115+ only — always provide a setTimeout fallback for full browser support.
  • Breaking up tasks has no effect on the total CPU work done — it only changes when that work happens, not how much. If the total work is excessive, removal or optimisation is also needed.

Common Mistakes

Yielding after every single item in a large loop

Yielding introduces overhead — a setTimeout per iteration of a 10 000-item loop creates 10 000 tasks. Yield between batches (every 50–100 items), not between individual items.

Deferring everything to requestIdleCallback

requestIdleCallback only runs during idle periods — on a busy page it may never fire, or fire too late. Use it only for genuinely low-priority, non-time-sensitive work like prefetching or analytics queuing.

Test this techniqueLong TasksRun the tool

Need help applying this to your site?

Targeted fixes, not guesswork. We identify which techniques move the needle for your specific WordPress stack.

Start an Engagement
Support our family now