Here are the steps:
Adding console.log(1) to the JS call stack. time(~0)
Executing it. (prints 1 in console) - time(~0)
Adding setTimeout(function(){console.log("2");},3000); to the call stack. - time(~0)
Move it to the event loop and start the timer. - time(3 sec)
As setTimeout is an asynchronous function it will move to the event loop.
Adding console.log(3) to the JS call stack. time(~0)
Executing it. (prints 3 in console) time(~0)
Adding setTimeout(function(){console.log("4");},1000); to the call stack. time(~0)
Move it to the event loop and start the timer. - time(1 sec)
The 1-second timer is finished so it will move back to the call stack and get executed.
Call Stack executes it. (prints 4 in console) - time(~0)
The 3-second timer is finished so it will move back to the call stack and get executed.
Call Stack executes it. (prints 2 in console) - time(~0)
Now what we are calling synchronous is JS call stack which can execute only one thing at a time.
I could have made it a 20 steps process but for ease of understanding 12 are enough.