🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
JS MASTER CLASS /// MASTER THE ENGINE /// BUILD LOGIC /// ASYNC PATTERNS /// JS MASTER CLASS /// MASTER THE ENGINE ///

Microtasks | JavaScript Tutorial - In-Depth Guide

Focus specifically on microtasks: what creates them (Promise callbacks, queueMicrotask, MutationObserver), their fully-drained execution guarantee, and practical use cases for scheduling one directly.

Total XP: 0|💻 javascript XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

System Hub

Core logic.

Quick Quiz //

What is the advantage of queueMicrotask() over `Promise.resolve().then(fn)` purely for scheduling?


🚀 LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
🎓 COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

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, microtask
localhost:3000
🔬

The 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, 3
localhost:3000

queueMicrotask()

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'));
});
localhost:3000

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);
  }
}
localhost:3000

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
}
localhost:3000

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

ChromeSupported

Fully supported.

FirefoxSupported

Fully supported.

SafariSupported

Fully supported.

EdgeSupported

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

THE BUG

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.

THE FIX

Wrap the synchronous path in queueMicrotask() so the callback always fires asynchronously, giving callers one consistent timing model to reason about.

THE BUG

A recursive function that keeps calling itself via queueMicrotask() with no exit condition, freezing the page since the microtask queue never finishes draining.

THE FIX

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
  });
}

Interview Prep

?Frequently Asked Questions

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

An unbounded microtask loop freezing the page

if (shouldContinue) queueMicrotask(loop); // must have a real exit condition

The Solution //

Add a proper exit condition, or switch to setTimeout for iterative work that needs to periodically yield control.

Lesson Glossary

[01]Microtask

A short function scheduled to run immediately after current code, with priority over macrotasks/rendering.

Code Preview
Promise callback

[02]queueMicrotask()

Directly schedules a function to run as a microtask.

Code Preview
queueMicrotask(fn)

[03]MutationObserver Callbacks

Another native source of microtasks, alongside Promise callbacks and queueMicrotask.

Code Preview
MutationObserver

[04]Microtask Starvation

A page freeze caused by an unbounded chain of self-scheduling microtasks preventing rendering.

Code Preview
infinite microtask loop

[05]Consistent Async Timing

Guaranteeing a callback always runs asynchronously, even when a synchronous shortcut is available, to avoid caller confusion.

Code Preview
forced via queueMicrotask

Continue Learning