Skip to content
Advertisement

How can Promise.resolve().then execute later?

Using native Javascript Promise:

Promise.resolve(1).then(it => console.log(it))
console.log(2)

This is logged:

2
1

Question: how is it possible for 2 to execute before 1? JS being event-driven, what is the event that is executing the callback given to then when the original caller has already left that execution tree? Is the engine doing some kind of behind-the-scene magic here?

Advertisement

Answer

JavaScript maintains something called a callstack. This is used to keep track of whereabouts in the script we are. When you call a function, it gets added to the callstack, and when the function returns/finishes, it gets removed/popped off the callstack. It is also helpful to think of your entire script also as being in its own “function”, and so, when your script first begins its executing, we add “script” to the callstack:

Stack:
- Script

When your Promise resolves, it executes its associated .then() method and adds the callback to something called the micro-task queue. This queue (along with the macro-task queue) is used so that JavaScript can manage asynchronous events. As a result, once you run:

Promise.resolve(1).then(it => console.log(it))

the state of your queues/stacks looks like so (note, this is the state after Promise.resolve(1) and .then() have been added/popped off the callstack):

Stack:
- Script

Micro task queue:
- it => console.log(it)

The callback in the micro-task queue will only execute once it gets added to the Stack. This happens through the use of the event-loop. The event-loop will pull tasks off the micro-task queue only when the callstack is empty. Currently, we are still running “script”, so the callstack isn’t empty yet.

Next, you encounter your console.log(2), as this is a function call, it gets added to the call stack, and once it returns it gets popped off. This is the reason why you see 2 in the console appear first, as the Promise’s .then() callback hasn’t executed yet, as it’s sitting in the micro-task queue waiting for the main script to finish. One the main script finishes, “script” gets popped off the stack:

Stack:
- (empty) <----------------<
                           | --- gets moved to the stack by the event-loop
Micro task queue:          |
- it => console.log(it) ---^

the event loop then moves the task(s) from the micro-task queue now that the callstack is empty. Once the task is moved to the callstack, it executes, running the second console.log(it). This is why you see 1 logged in the console after the 2.

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement