There are 2 separate queues for handling of the callbacks. A macro and a micro queue. setTimeout
enqueues an item in the macro queue, while promise resolution - to the micro queue. The currently executing macro task (the main script itself in this case) is executed synchronously, line by line until it is finished. The moment it is finished, the loop executes everything queued in the microtask queue before continuing with the next item from the macro queue (which in your case is the console.log("hello")
queued from the setTimeout
).
Basically, the flow looks like this:
- Script starts executing.
MacrotaskQueue: [], MicrotaskQueue: [].
setTimeout(() => console.log("hello"), 0);
is encountered which leads to pushing a new item in the macrotask queue.
MacrotaskQueue: [console.log("hello")
], MicrotaskQueue: [].
Promise.resolve('Success!').then(console.log)
is read. Promise resolves to Success!
immediately and console.log
callback gets enqueued to the microtask queue.
MacrotaskQueue: [console.log("hello")
], MicrotaskQueue: [console.log('Success!')
].
- The script finishes executing so it checks if there is something in the microtask queue before proceeding with the next task from the macro queue.
console.log('Success!')
is pulled from the microtask queue and executed.
MacrotaskQueue: [console.log("hello")
], MicrotaskQueue: [].
- Script checks again if there is something else in the microtask queue. There is none, so it fetches the first available task from the macrotask queue and executes it, namely -
console.log("hello")
.
MacrotaskQueue: [], MicrotaskQueue: [].
- After the script finishes executing the
console.log("hello")
, it once again checks if there is anything in the microtask queue. It is empty, so it checks the macrotask queue. It is empty as well so everything queued is executed and the script finishes.
This is a simplified explanation, though, as it can get trickier. The microtask queue normally handles mainly promise callbacks, but you can enqueue code on it yourself. The newly added items in the microtask queue will still be executed before the next macrotask item. Also, microtasks can enqueue other microtasks, which can lead to an endless loop of processing microtasks.
Some useful reference resources: