Browsers implement multiple job/task queues. The spec requires implementations to maintain two queues:
- ScriptQueue
- PromiseQueue
There is most likely a TimerQueue (HTML DOM Task Queue) that accommodates the HTML DOM Level 2 Timer spec too. These are each FIFO queues that are populated at runtime and eventually are the event loop queue. Following your code example:
setTimeout(()=> console.log('1'));
Promise.resolve('whatever').then(()=>console.log('2'));
console.log('3');
- Line 1 pushes onto (likely) a TimerQueue (HTML DOM Task Queue)
- Line 2 pushes onto the
PromiseQueue
- Line 3 pushed onto the stack and is executed immediately
(to completion)
Once the stack is empty, each queue will drain until empty. In your example, the Promise queue empties first and then the TimerQueue empties last.
This can be further demonstrated by extending your example just a bit:
setTimeout(()=> console.log('second from final')); // <-- first item in TimerQueue
Promise.resolve('whatever').then(()=>console.log('2')); //<-- first time in PromiseQueue
setTimeout(()=> console.log('just prior to final')); // <-- second item in TimerQueue
Promise.resolve('whatever').then(()=>console.log('1')); //<-- second item in PromiseQueue
setTimeout(()=> console.log('final')); // <-- third item in ScriptQueue
console.log('3'); // <-- synchrounous function call placed onto the stack, executed immediately
You should note that order of execution is not guaranteed. The spec does not define the execution order of the queues:
This specification does not define the order in which multiple Job
Queues are serviced. An ECMAScript implementation may interweave the
FIFO evaluation of the PendingJob records of a Job Queue with the
evaluation of the PendingJob records of one or more other Job Queues.
EDIT
After discussion (below) with Bergi I have indicated that browser implementations likely create other queues too. The most likely queue related to this post would be a TimerQueue as a Task Queue that holds the HTML DOM spec'd timer tasks.