October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk3 min

JavaScript Promises: What Runs Now, What Waits, and Why

Promise executors run immediately; their reactions run later. Learn how microtasks, await, rejection handling, and Promise combinators fit together.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A Promise’s executor runs immediately, but its .then() handlers run later. In browsers, those handlers are queued as microtasks, which run after the current JavaScript finishes and before the event loop takes another task. That distinction explains why Promises can defer reactions without moving ordinary JavaScript work onto another thread.

What a Promise represents—and what it does not

A Promise is a JavaScript object representing an outcome that may become fulfilled or rejected. It can track the result of work that completes later, such as a network request, but the Promise itself does not make that work asynchronous or run it on a separate thread.

As an Amazon Associate I earn from qualifying purchases.

When code calls new Promise(executor), JavaScript invokes executor immediately. The executor may start an operation that completes later. Calling its resolve or reject function settles the Promise, or locks it to follow another Promise or thenable. Reactions registered with .then() are delivered later, not as part of the immediate executor call.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why a Promise reaction runs after synchronous code

Consider this browser example:

console.log("sync start");

Promise.resolve().then(() => console.log("promise reaction"));
setTimeout(() => console.log("timer task"), 0);

console.log("sync end");

The output is:

sync start
sync end
promise reaction
timer task

The first two logs execute in the current JavaScript job. The Promise reaction is queued as a microtask, while the timer callback is a task. In the browser event-loop model, after the current job completes, microtasks are processed before the event loop selects another task. The timer’s 0 does not mean an exact delay or guarantee a fixed callback time.

As MDN Web Docs puts it: “To avoid surprises, functions passed to then() will never be called synchronously, even with an already-resolved promise:” MDN’s Promise guide explains this behavior and the interaction between Promise reactions and tasks.

Microtasks defer work; they do not create another thread

A microtask runs after the current job, but it still runs JavaScript on the same agent. Each job runs to completion. A long synchronous calculation—or a long chain of microtasks—can therefore delay input handling, timers, and other callbacks. Promises are useful for organizing asynchronous results, but they are not a way to make CPU-heavy JavaScript run in parallel.

The Promise and job semantics belong to JavaScript, while the event loop and its task and microtask scheduling are provided by the host environment. The browser ordering above describes the ordinary web-platform case; Node.js has additional scheduling details, so browser ordering should not be generalized to every host. See MDN’s JavaScript execution model for the distinction.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What await pauses

await suspends the rest of the async function containing it until the awaited value settles. It does not block the caller or freeze the whole program. The caller and unrelated work can continue while that function is waiting. When the Promise fulfills, the await expression evaluates to its fulfillment value; if it rejects, the rejection is thrown at the await point.

async function loadLabel() {
  try {
    const label = await getLabel();
    console.log(label);
  } catch (error) {
    console.error("Could not load label", error);
  }
}

loadLabel();
console.log("caller continues");

The caller can log before loadLabel resumes. Even awaiting an already-fulfilled Promise defers the async function’s continuation rather than blocking the main thread. MDN’s documentation for await describes how fulfillment values and rejections are handled.

How rejection moves through Promise code

A rejection is the failure path of a Promise chain. Use .catch() to handle a rejection in a chain, or wrap an await in try/catch to handle it as a thrown value. If a rejection is not handled, it remains an unhandled rejection; the host may report it.

getLabel()
  .then(label => label.toUpperCase())
  .catch(error => {
    console.error("Could not load label", error);
  });

Handling a rejection also affects the chain’s outcome: a handler that returns a normal value allows the next Promise in the chain to fulfill with that value, while a handler that throws or returns a rejected Promise keeps the failure path going. For the core Promise behavior, see MDN’s Promise reference.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Choose a Promise combinator by its completion rule

Promise combinators coordinate outcomes; they are not parallel-execution commands. Independent operations can be pending at the same time, but a JavaScript agent executes one job at a time.

Method When the aggregate settles Rejection behavior
Promise.all() After every input fulfills Rejects when an input rejects
Promise.allSettled() After every input settles Fulfills with each input’s outcome, whether fulfilled or rejected
Promise.any() When the first input fulfills Rejects if all inputs reject
Promise.race() When the first input settles Adopts that first outcome, fulfillment or rejection

Choose all when every result is needed and any failure should fail the aggregate; choose allSettled when every outcome matters, including failures. Use any when one successful result is enough, or race when the first outcome of either kind should determine the aggregate.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.