Recently I've been given a following problem:
console.log("start");
const promise1 = Promise.resolve().then(() => {
console.log("promise1");
const timer2 = setTimeout(() => {
console.log("timer2");
}, 0);
});
const timer1 = setTimeout(() => {
console.log("timer1");
const promise2 = Promise.resolve().then(() => {
console.log("promise2");
});
}, 0);
console.log("end");
My initial answer was start, end, promise1, timer2, promise2, timer1
(wrong one).
I understand that synchronous code executes first then microtasks and then macrotasks. I also understand that microtask may appear between two macrotasks. I'm slightly confused and not fully aware why timer2
wasn't queued by macrotask when we were executing promise1
in a callstack. I also understand that JS engine read and queued timer1
while we were processing promise1
, but can't fully understand the reason for it.
To me it seems like globally declared values are read first.
Below is the principle that solves the order of a problem:
By this principle, it seems that global values are considered first. I understand the logic behind the answer, but can't find any answers on whether "global values are considered first statement" is true. I'm not sure why timer2
wasn't queued when we processed promise1
. I somewhat believe it was queued but timer1
was processed faster than timer2
since it is declared globally . Was timer1
read first and queued before timer2
because it is in global scope? What is the reason for it? I somewhat understand it but not entirely sure.
but can't find any answers on whether "global values are considered first statement" is true. I'm not sure why timer2 wasn't queued when we processed promise1. I somewhat believe it was queued but timer1 was processed faster than timer2 since it is declared globally . Was timer1 read first and queued before timer2 because it is in global scope?
It's not really about scope, but order of execution. As you mentioned, synchronous code happens first, and part of that synchronous code is to set up timer1. Setting up timer2 is not part of the synchronous code; it happens later.
Here's the sequence that happens. First, the synchronous stuff:
.then
on it to make promise1.Now that that's done, code execution returns. There is one microtask waiting to run (the .then
related to promise1), so it runs next.
Now that's done, and there's nothing else to be done for a little while. After the minimum timer delay elapses, timers start going off. I'm not sure if the 2 tasks get inserted into the queue at exactly the same time or slightly separated in time. But the order of the tasks will always be the same (same order they were created, given they have the same delay), so it won't affect the output that's logged. Let's assume both macrotasks get queued simultaneously. Thus we have 2 macro tasks in the queue, and it handles the first one, which is for timer1:
.then
on it to create promise2When this returns, there is one microtask waiting to run (the .then
related to promise2) and one macrotask (timer 2). The microtask runs first:
When this returns, there is one macrotask waiting to run, so that runs next:
Was timer1 read first and queued before timer2 because it is in global scope?
It's not really scope related, it's just that creating timer1 is part of the synchronous code, and creating timer2 only happens later.