Any JavaScript task running for more than 50ms continuously blocks the browser's main thread (ISO-25010 time-behaviour). During that time the browser cannot respond to user input, run animations, or update the UI. The result is a UI that feels frozen or laggy — users experience input lag even if no interaction is happening, because the browser is busy with work they did not trigger. Long tasks are the most common root cause of poor INP and Total Blocking Time scores, and they compound when multiple libraries initialize sequentially at page load.
High because tasks over 50ms directly block the main thread, preventing the browser from responding to user input and degrading INP — a Core Web Vitals ranking signal.
Break tasks exceeding 50ms into smaller chunks the browser can interleave with rendering. Use scheduler.yield() for sequential processing:
// src/lib/process.ts
async function processArray(items: Item[]) {
for (let i = 0; i < items.length; i++) {
processItem(items[i])
if (i % 50 === 0) await scheduler.yield() // yield every 50 items
}
}
For one-off heavy operations, move them to a Web Worker entirely:
// main thread
const worker = new Worker(new URL('./processor.worker.ts', import.meta.url))
worker.postMessage({ items: largeArray })
worker.onmessage = (e) => displayResults(e.data)
Defer non-critical initialization to idle time:
requestIdleCallback(() => initAnalytics())
Identify long tasks in Chrome DevTools Performance tab — they appear as red-flagged blocks over 50ms in the Main thread row.
ID: performance-core.rendering-paint.long-tasks
Severity: high
What to look for: Count all relevant instances and enumerate each. Profile the main thread using DevTools Performance tab or build output. Look for JavaScript execution that blocks for over 50ms continuously. Check for heavy computations, parsing, encryption, or data processing that runs synchronously. Identify third-party scripts and their execution times.
Pass criteria: Long tasks are either broken up with scheduler.yield() or moved to Web Workers. Main thread shows consistent execution under 50ms blocks. Third-party scripts are lazy-loaded or executed asynchronously.
Fail criteria: Long tasks (over 50ms) block main thread regularly. Heavy computations (parsing, encryption, sorting) run synchronously during user interactions. Profiling shows sustained main thread blocking.
Skip (N/A) when: No user interaction or animations that depend on main thread performance.
Detail on fail: Specify the long task and duration. Example: "Data table initialization blocks for 180ms loading 10K rows. React reconciliation on large state change takes 120ms; component re-renders unnecessarily" or "Three sequential heavy computations in lifecycle: font face set load (50ms), analytics setup (40ms), widget init (35ms)".
Remediation: Tasks over 50ms block user interaction for that duration. Break them up:
Use scheduler.yield() to interleave with browser updates:
async function processArray(items) {
for (let i = 0; i < items.length; i++) {
processItem(items[i])
if (i % 50 === 0) await scheduler.yield() // let browser paint
}
}
Move CPU-heavy work to Web Worker:
// main.js
const worker = new Worker('processor.worker.js')
worker.postMessage({ items })
worker.onmessage = (e) => displayResults(e.data)
Use requestIdleCallback for non-critical work:
requestIdleCallback(() => initializeOptionalFeatures())