Call Stack, Callback Queue, Microtask Queue & Libuv Workers0%

Call Stack, Callback Queue, Microtask Queue & Libuv Workers

Beginner12 min read•Updated: Jul 11, 2026
Study MaterialsCheatsheet

Call Stack, Callback Queue, Microtask Queue & Libuv Workers

To master backend performance and debug tricky race conditions in Node.js, every developer must understand the exact priority mechanism governing asynchronous execution: the Call Stack, the Microtask Queue, the Callback Queue (Macrotask Queue), and the Libuv Worker Thread Pool.


1. Execution Priority Hierarchy

When multiple asynchronous operations complete simultaneously, Node.js uses a strict order of precedence:

  1. 1
    Synchronous Code on Call Stack: Runs immediately until the call stack is completely empty.
  2. 2
    Microtask Queue 1 (process.nextTick): Runs before ANY other asynchronous queue, immediately after the current operation finishes.
  3. 3
    Microtask Queue 2 (Promises / queueMicrotask): Promise .then(), .catch(), and async/await continuations.
  4. 4
    Macrotask / Timer Queue (setTimeout, setInterval): Evaluated during the Timers phase of the Event Loop.
  5. 5
    Check Queue (setImmediate): Executed during the Check phase of the Event Loop.
Output
[Call Stack] -> [process.nextTick queue] -> [Promise Microtasks] -> [Event Loop Phases]

2. Priority Demonstration in Code

JavaScript
console.log('1. Synchronous');
 
setTimeout(() => {
console.log('5. setTimeout (Timer Macrotask)');
}, 0);
 
setImmediate(() => {
console.log('6. setImmediate (Check Macrotask)');
});
 
Promise.resolve().then(() => {
console.log('3. Promise.then (Microtask Queue)');
});
 
process.nextTick(() => {
console.log('2. process.nextTick (Microtask Queue - Priority 1)');
});
 
console.log('4. Synchronous end');

Actual Execution Order:

Output
1. Synchronous
4. Synchronous end
2. process.nextTick (Microtask Queue - Priority 1)
3. Promise.then (Microtask Queue)
5. setTimeout (Timer Macrotask)
6. setImmediate (Check Macrotask)

3. The Libuv Worker Thread Pool

While network I/O (HTTP sockets, TCP) uses non-blocking OS kernel polling (epoll/kqueue), certain operations cannot be performed asynchronously by operating system kernels. Node.js sends these to the Libuv Thread Pool:

  • File System Operations: fs.readFile, fs.writeFile
  • Cryptography: crypto.pbkdf2, crypto.scrypt, crypto.randomBytes
  • Compression: zlib.deflate, zlib.gzip
  • DNS Lookups: dns.lookup

The default size of this thread pool is 4 threads, but it can be scaled for heavy workloads using the environment variable:

Bash / Terminal
# Linux / macOS
export UV_THREADPOOL_SIZE=16
 
# Windows PowerShell
$env:UV_THREADPOOL_SIZE=16

Interactive Quiz

Multiple Choice Questions

Question 1
Which asynchronous task queue has the highest priority and executes immediately after the current call stack clears, before Promise resolutions?
Question 2
What is the default number of worker threads in the Libuv thread pool in Node.js?
Question 3
Which of the following operations is offloaded to the Libuv worker thread pool rather than using OS-level non-blocking kernel sockets?
Question 4
What is the danger of recursively calling process.nextTick() inside its own callback?
Question 5
Between setTimeout(fn, 0) and setImmediate(fn), which is explicitly designed to execute immediately after the I/O polling phase completes?

Next Lesson

Non-Blocking Asynchronous Code with Promises and Async/Await

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext