scheduler.postTask(): Prioritized Task Queues (user-blocking, user-visible, background)
- Last updated
- Prerequisites:
- Browser Event loop macrotasks and microtasks
- Interaction to Next Paint (INP)
- browser
- scheduler
- posttask
- performance
- event-loop
- prioritization
Read at your depth
The practical view
`scheduler.postTask(callback, options)` is the modern standard for scheduling prioritized asynchronous tasks on the main thread. It replaces ad-hoc hacks like `setTimeout(fn, 0)`, `requestIdleCallback()`, and `MessageChannel`. It provides three distinct priority levels: `'user-blocking'` (highest: urgent rendering/input), `'user-visible'` (default: normal rendering), and `'background'` (lowest: analytics, prefetching).
The same idea in other frameworks
React Concurrent Mode scheduler (`Scheduler.js`) internal priority lanes.Zoneless change detection scheduling microtasks vs postTask batches.Vue component async chunk pre-fetching in idle time.Svelte transition scheduling prioritizing active DOM animations.Legacy vs modern
Unprioritized setTimeout Hacks vs scheduler.postTask
`setTimeout(fn, 0)` lumps background analytics in the same queue as urgent UI work; `scheduler.postTask` gives explicit priority control.
setTimeout(sendAnalytics, 0); // ❌ Competes equally with urgent user renderingscheduler.postTask(sendAnalytics, { priority: 'background' }); // ✅ NEWInterview gotchas
Context
Tests knowledge of the Prioritized Task Scheduling API specification.
The mechanical answer
The three priority levels are: (1) `'user-blocking'` for critical tasks that directly block the user from interacting (e.g. rendering immediately after a click), (2) `'user-visible'` (the default) for normal UI updates that are visible to the user but not critical for input latency, and (3) `'background'` for non-urgent tasks like analytics logging, data prefetching, or indexing.
Trap
Do not confuse `scheduler.postTask` with microtasks (`Promise.then`). `postTask` schedules tasks into the *task (macrotask)* queue system, not the microtask queue.
Context
This tests advanced usage of `TaskController`.
The mechanical answer
Use a `TaskController` (which extends `AbortController`): `const controller = new TaskController({ priority: 'background' });`. Pass its signal: `scheduler.postTask(task, { signal: controller.signal })`. To elevate priority, call `controller.setPriority('user-blocking')`. To cancel, call `controller.abort()`.
Trap
A standard `AbortController` can cancel a `postTask`, but only a `TaskController` can dynamically update priority via `setPriority()`.