Understanding Promises and Coroutines in JavaScript

As a JavaScript developer, you've probably heard terms like Promise, async/await, and maybe even coroutines thrown around a lot. Let’s break them down in a human way - no fancy words, just real-world explanations. What is a Promise? Think of a Promise as a placeholder for a value that you’ll get later. Let’s say you order a pizza. You get a receipt — that's the promise. You don’t have the pizza yet, but the promise says, “Don’t worry, it’s coming.” In code: const pizzaOrder = new Promise((resolve, reject) => { setTimeout(() => { const isPizzaReady = true; if (isPizzaReady) { resolve("Pizza is ready!"); } else { reject("Sorry, no pizza today."); } }, 3000); }); pizzaOrder .then((message) => console.log(message)) .catch((error) => console.error(error)); Here, .then() is like, “What do I do when the pizza arrives?” and .catch() is, “What if it doesn’t?” Why Do We Use Promises? JavaScript is single-threaded — it can do one thing at a time. Promises help us avoid blocking things while waiting for slower tasks (like API calls or timers). Instead of freezing the app, Promises let JavaScript say, “I’ll come back to this when it’s ready,” and keep moving. Async/Await = Cleaner Promises Dealing with .then() and .catch() can get messy, especially with many steps. So, JavaScript gave us a cleaner syntax: async and await. async function makeDinner() { try { const pizza = await pizzaOrder; console.log(pizza); // "Pizza is ready!" } catch (error) { console.error(error); } } await tells JavaScript to pause until the promise is done — but it only works inside an async function. This is where things start to feel like “coroutines.” What’s a Coroutine? In simple terms, a coroutine is a function that can pause and resume its execution. JavaScript doesn’t have built-in coroutines like Python does with yield, but async/await gives us similar power. With await, your function looks synchronous (step-by-step), but it actually runs asynchronously under the hood. Coroutine Mechanics via Generators Yielding Values: function* greeter() { yield "Hi"; yield "Hello"; return "Bye"; } const it = greeter(); console.log(it.next()); // { value: "Hi", done: false } console.log(it.next()); // { value: "Hello", done: false } console.log(it.next()); // { value: "Bye", done: true } You can send values back into a coroutine using next(value). async/await is JavaScript's native coroutine syntax. It abstracts away yield and generator handling. Under the hood, async functions return a Promise and behave like auto-resuming coroutines. When Should You Use Promises or Async/Await? Use Promise when: You want a task to run in the background. You need to chain steps (.then().then().catch()). Use async/await when: You want cleaner, more readable async code. You need to handle multiple async tasks step-by-step. Final Thoughts Promises and async/await make writing asynchronous code in JavaScript a lot easier than it used to be. Think of them as your way to say, “Do this thing, but let’s not freeze the app while we wait.” If you're comfortable with Promises and async/await, you already know how to use coroutines - even if JavaScript doesn’t officially call them that.

May 9, 2025 - 15:02
 0
Understanding Promises and Coroutines in JavaScript


As a JavaScript developer, you've probably heard terms like Promise, async/await, and maybe even coroutines thrown around a lot. Let’s break them down in a human way - no fancy words, just real-world explanations.

What is a Promise?

Think of a Promise as a placeholder for a value that you’ll get later.
Let’s say you order a pizza. You get a receipt — that's the promise. You don’t have the pizza yet, but the promise says, “Don’t worry, it’s coming.”
In code:

const pizzaOrder = new Promise((resolve, reject) => {
  setTimeout(() => {
    const isPizzaReady = true;
    if (isPizzaReady) {
      resolve("Pizza is ready!");
    } else {
      reject("Sorry, no pizza today.");
    }
  }, 3000);
});

pizzaOrder
  .then((message) => console.log(message))
  .catch((error) => console.error(error));

Here, .then() is like, “What do I do when the pizza arrives?” and .catch() is, “What if it doesn’t?”

Why Do We Use Promises?

JavaScript is single-threaded — it can do one thing at a time. Promises help us avoid blocking things while waiting for slower tasks (like API calls or timers).

Instead of freezing the app, Promises let JavaScript say, “I’ll come back to this when it’s ready,” and keep moving.

Async/Await = Cleaner Promises

Dealing with .then() and .catch() can get messy, especially with many steps.
So, JavaScript gave us a cleaner syntax: async and await.

async function makeDinner() {
  try {
    const pizza = await pizzaOrder;
    console.log(pizza); // "Pizza is ready!"
  } catch (error) {
    console.error(error);
  }
}

await tells JavaScript to pause until the promise is done — but it only works inside an async function.
This is where things start to feel like “coroutines.”

What’s a Coroutine?

In simple terms, a coroutine is a function that can pause and resume its execution.
JavaScript doesn’t have built-in coroutines like Python does with yield, but async/await gives us similar power.
With await, your function looks synchronous (step-by-step), but it actually runs asynchronously under the hood.

Coroutine Mechanics via Generators

Yielding Values:

function* greeter() {
  yield "Hi";
  yield "Hello";
  return "Bye";
}

const it = greeter();
console.log(it.next()); // { value: "Hi", done: false }
console.log(it.next()); // { value: "Hello", done: false }
console.log(it.next()); // { value: "Bye", done: true }

You can send values back into a coroutine using next(value).

async/await is JavaScript's native coroutine syntax. It abstracts away yield and generator handling.
Under the hood, async functions return a Promise and behave like auto-resuming coroutines.

When Should You Use Promises or Async/Await?

Use Promise when:

  1. You want a task to run in the background.
  2. You need to chain steps (.then().then().catch()).

Use async/await when:

  1. You want cleaner, more readable async code.
  2. You need to handle multiple async tasks step-by-step.

Final Thoughts

Promises and async/await make writing asynchronous code in JavaScript a lot easier than it used to be. Think of them as your way to say, “Do this thing, but let’s not freeze the app while we wait.”

If you're comfortable with Promises and async/await, you already know how to use coroutines - even if JavaScript doesn’t officially call them that.