Microtasks are the highest-priority scheduling mechanism in JavaScript — understanding exactly what qualifies as a microtask, and using queueMicrotask() directly, unlocks precise control over execution timing.
1Microtasks | JavaScript Tutorial - In-Depth Guide Part 1
A microtask is a short function scheduled to run immediately after the currently executing script finishes, but before the event loop moves on to rendering or the next macrotask.
console.log('sync');
Promise.resolve().then(() => console.log('microtask'));
console.log('sync 2');
// Output: sync, sync 2, microtaskThe High-Priority Queue
2Microtasks | JavaScript Tutorial - In-Depth Guide Part 2
queueMicrotask(callback) schedules a microtask directly, without needing to create or resolve a Promise purely as a scheduling trick.
console.log('1');
queueMicrotask(() => console.log('3'));
console.log('2');
// Output: 1, 2, 3queueMicrotask()
3Microtasks | JavaScript Tutorial - In-Depth Guide Part 3
The microtask queue is guaranteed to fully drain — including any NEW microtasks scheduled while draining — before the event loop does anything else, like rendering or running a macrotask.
queueMicrotask(() => {
console.log('first microtask');
queueMicrotask(() => console.log('nested microtask, still drains now'));
});Guaranteed Full Drain
4Microtasks | JavaScript Tutorial - In-Depth Guide Part 4
A practical use for queueMicrotask(): ensuring a callback always runs asynchronously, even when the value it depends on is already available synchronously.
function subscribe(callback, cachedValue) {
if (cachedValue !== undefined) {
// Force async, even though we already have the value:
queueMicrotask(() => callback(cachedValue));
} else {
fetchValue().then(callback);
}
}Guaranteeing Consistent Async Timing
5Microtasks | JavaScript Tutorial - In-Depth Guide Part 5
Excessive or unbounded microtask scheduling can starve the browser from rendering or handling user input, since the entire queue must drain before anything else happens.
// Dangerous: an unbounded microtask loop can freeze the page
function loop() {
queueMicrotask(loop); // never yields to rendering or macrotasks
}The Starvation Risk
6Step-by-Step Breakdown
A microtask is a short function scheduled to run immediately after the currently executing script finishes, but before the event loop moves on to rendering or the next macrotask.
queueMicrotask(callback) schedules a microtask directly, without needing to create or resolve a Promise purely as a scheduling trick.
Checkpoint: What is the advantage of queueMicrotask() over Promise.resolve().then(fn) purely for scheduling?
- →It expresses microtask-scheduling intent directly, without creating an unnecessary Promise
- →It runs with higher priority than a Promise .then() callback
The microtask queue is guaranteed to fully drain — including any NEW microtasks scheduled while draining — before the event loop does anything else, like rendering or running a macrotask.
A practical use for queueMicrotask(): ensuring a callback always runs asynchronously, even when the value it depends on is already available synchronously.
Excessive or unbounded microtask scheduling can starve the browser from rendering or handling user input, since the entire queue must drain before anything else happens.
Checkpoint: Can an unbounded chain of self-scheduling microtasks prevent the browser from rendering?
- →Yes, since the microtask queue must fully drain before rendering
- →No, rendering always happens between individual microtasks
Next, we'll explore 'Macrotasks'.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Don't Let Microtask Starvation Delay Time-Sensitive Accessibility Feedback
Since a runaway microtask chain can prevent the browser from rendering or processing input, ensure any code responsible for timely accessible feedback (like focus movement or live region updates) isn't competing with, or blocked by, unbounded microtask scheduling elsewhere in the app.
SEO Implications
- 1
No Direct SEO Effect
Microtask scheduling is a low-level execution-timing concept; SEO relevance is limited to avoiding page-freezing bugs from runaway microtask chains.
Best Practices
Use queueMicrotask() Directly Instead of an Empty Promise Chain for Microtask Timing
It's more efficient and communicates intent more clearly than the older `Promise.resolve().then(fn)` workaround.
Guarantee Consistent Async Timing for Callback-Based APIs
An API whose callback sometimes runs synchronously and sometimes asynchronously (depending on caching or timing) is a common source of subtle bugs; forcing consistently async timing with queueMicrotask() avoids this.
Frequent Bugs
An API design flaw where a callback sometimes fires synchronously (if data is cached) and sometimes asynchronously (if a fetch is needed), causing calling code that assumes one behavior to break intermittently.
Wrap the synchronous path in queueMicrotask() so the callback always fires asynchronously, giving callers one consistent timing model to reason about.
A recursive function that keeps calling itself via queueMicrotask() with no exit condition, freezing the page since the microtask queue never finishes draining.
Add a proper termination condition, or intentionally use setTimeout instead of queueMicrotask() to yield control back to the browser periodically for genuinely long-running iterative work.
Real-World Examples
Ensuring Consistent Callback Timing in an Event Emitter
A custom event emitter's subscribe callback sometimes fired synchronously (for already-emitted events) and sometimes asynchronously, confusing consumers who assumed one consistent behavior.
function emit(event, cachedListeners) {
cachedListeners.forEach((listener) => {
queueMicrotask(() => listener(event)); // always async, consistently
});
}