Async/Await Wasn't Magic, It Was Just "Reordering"

A deep dive into the synchronous nature of Promises and how the Microtask Queue handles execution order.

January 22, 2026 •

Code đź’»

I have typed async/await countless times while writing JavaScript code. But if I’m being honest, I relied more on the result—"writing it this way makes it wait"—rather than understanding exactly how these keywords function inside the engine.

"Doesn't using a Promise offload the work to a background thread?" "Does using Await freeze the browser?"

Today, as I dove deep into the mechanics of asynchronous operations, I realized my mental model was completely wrong. Here is a record of the "real" face of JavaScript concurrency—specifically regarding the synchronous nature of Promises and sequence reordering via the Microtask Queue.

1. A Promise is Not "Multi-threading" (The Secret of Immediate Execution)

The biggest misconception I had was thinking that using a Promise made JavaScript act like a multi-threaded language, offloading work to a worker. But JavaScript is, and remains, single-threaded.

Let's look at the getCoffee function example.

function getCoffee() {
  return new Promise((resolve) => {
    // ⚠️ Note: This part runs IMMEDIATELY when called, not in the background.
    console.log('>> [Kitchen] Order received! (Inside Promise)');

    // The only thing that truly goes to the background (Web API) is setTimeout.
    setTimeout(() => {
      resolve('Americano');
    }, 1000);
  });
}

console.log('1. Enter Shop');
getCoffee();
console.log('2. Sit down');

Execution Result:

1. Enter Shop
>> [Kitchen] Order received! (Inside Promise)
2. Sit down

Surprisingly, the [Kitchen] log appears before "Sit down". This is because the code inside the Promise constructor is executed synchronously (immediately) the moment it is created. A Promise is not a "worker" that does the job for you; it is merely a "buzzer" object that promises to notify you when the result is ready.

2. The Reality of Await: Cutting Code with Scissors (Microtask Queue)

So, how does await actually work? I thought it was simply a "pause" command, but in reality, it is a technique that cuts the code in half and manipulates the execution order.

The moment the JavaScript engine encounters await, the code gets Split.

async function order() {
  console.log('Order placed');

  // --- ✂️ The Cut Happens Here (Await) ✂️ ---
  await getCoffee();

  // [The Bottom Half] -> Moved to the Microtask Queue
  console.log('Coffee served');
}

console.log('Start');
order();
console.log('Do something else');

Tracing the execution order of this code reveals the true nature of await:

  1. Synchronous Zone: console.log("Order placed") and the call to getCoffee() are executed immediately without stopping.
  2. The Cut & Registration: The moment await is met, the function's execution pauses. The remaining code below it (console.log("Coffee served")) is bundled up and sent to a waiting area called the Microtask Queue.
  3. Reordering: The JavaScript engine continues to process what's currently on the Call Stack. Thus, console.log("Do something else") runs first.
  4. Final Execution: Only after all synchronous code is finished and the stack is empty does the engine retrieve the "Coffee served" code from the queue and execute it.

3. "Technically, It Runs Last"

What we commonly refer to as "running asynchronously" is, technically speaking, equivalent to "deferring priority to the very end."

No matter how quickly resolve happens, the code following an await enters the Microtask Queue. Therefore, if there is even a single line of synchronous code left to run, the awaited code will never execute. It’s essentially a "polite mode."

  • Me (JS Engine): "I'll finish the easy work (synchronous code) first, and then handle the coffee (Microtask) I put aside earlier."

This is both the illusion and the truth of async/await. It looks like it’s running in parallel, but it is actually meticulously lining up tasks to defer them to the end.

4. Sequential vs. Parallel

While await makes code look synchronous and clean, using it mindlessly can kill performance.

// ❌ Inefficient method (Total: 5 seconds)
const coffee = await getCoffee(); // Idles for 3 seconds
const bread = await getBread(); // Idles for 2 seconds

// âś… Efficient method (Total: 3 seconds)
const [coffee, bread] = await Promise.all([
  getCoffee(), // Both start at the same time!
  getBread(),
]);

If you have a separate coffee machine and a bread oven, you should run them simultaneously. If the tasks are unrelated, the key is to use Promise.all to process them in parallel.

Conclusion

In the end, JavaScript's asynchrony was an "Illusion."

To overcome the limitation of being single-threaded, it relies on a sophisticated system of division of labor: "offloading long-running tasks to the browser (Web API)" and "deferring result processing to the Queue."

  1. Code inside a Promise constructor runs immediately.
  2. await cuts the code and sends the latter half to the Microtask Queue (the back of the line).
  3. Ultimately, asynchronous programming is an act of consideration: "I will defer my remaining work to the very end so as not to block the current task."

Invely's