Byte Corner

#javascript

•

4 min read

•

September 29, 2026

return Is Built on an Idea You Can See in yield

await and yield aren't the same mechanism, but they share a surprisingly important idea: execution can suspend and continue later.

Matej Bošnjak
Matej Bošnjak

September 29, 2026

#javascript
#async-await
#generators
`return` Is Built on an Idea You Can See in `yield`

async Is Built on an Idea You Can See in yield

yield and await aren't the same thing.

A generator isn't an async function, and an async function isn't a generator.

But there is an idea hiding underneath both of them that is easy to miss:

execution can stop here and continue later.

Once you see that, async/await starts looking a lot less magical.

🔴 The Problem

Consider a generator:

function* work() {
  console.log("A");

  yield;

  console.log("B");
}

Create it:

const task = work();

Nothing has run yet.

Now call next():

task.next();
// A

The generator runs until it reaches yield.

Then it stops.

It hasn't finished. It hasn't returned.

It's suspended.

Call next() again:

task.next();
// B

Execution continues exactly where it stopped.

That's unusual if you're used to ordinary functions.

Normally, you think of a function like this:

start → run → finish

A generator gives you:

start → run → suspend → resume → finish

And that middle part is the interesting bit.

If you want to see the whole thing together:

function* work() {
  console.log("A");

  yield;

  console.log("B");
}

const task = work();

task.next(); // A
task.next(); // B

The first next() runs until yield.
The second next() resumes the generator and continues after yield.

🟢 The Idea

yield makes suspension easy to see.

The generator essentially says:

"I'm stopping here. You can have control for a while. I'll continue later."

The caller decides when that happens:

const task = work();

task.next(); // runs until yield

// do something else

task.next(); // continue after yield

Nothing about this requires the generator to block JavaScript.

The generator's execution is paused while the rest of the program can continue.

That idea turns out to be extremely useful for asynchronous programming.

🔵 Promises Change Who Controls the Resume

Promises give us another way to express "continue later."

Without async/await, you might write:

doSomething().then((result) => {
  use(result);
});

The interesting part isn't really .then().

It's what the code is saying:

start something
       ↓
   not ready yet
       ↓
   continue later
       ↓
    use(result)

The function doing the work doesn't need to sit around waiting for the result.

Instead, the continuation is registered and runs when the Promise settles.

This is conceptually similar to what we saw with yield, even though the mechanisms are completely different.

With a generator, you explicitly resume the computation:

task.next();

With a Promise, settlement allows the continuation to be scheduled.

Different machinery.

Same useful idea:

this computation can suspend while something else happens.

🟣 Then await Makes It Look Normal

Now look at the same kind of operation using async/await:

async function work() {
  const result = await doSomething();

  use(result);
}

This looks almost like ordinary synchronous code.

That's intentional.

But the function isn't actually blocking at await.

When JavaScript reaches:

const result = await doSomething();

the async function suspends.

JavaScript gets control back.

When the Promise eventually fulfills, the async function continues:

use(result);

You can think of the execution like this:

work()
  ↓
run
  ↓
await Promise
  ↓
suspend
  ↓
JavaScript continues elsewhere
  ↓
Promise fulfills
  ↓
resume work()
  ↓
use(result)

The await marks a point where this particular piece of execution may need to give up control temporarily.

The rest of JavaScript doesn't have to wait with it.

⚡ The "Why"

Here's where yield becomes useful for understanding await.

With a generator, we can see the suspension point directly:

function* generator() {
  doSomething();

  yield;

  continueWork();
}

yield explicitly tells the generator:

"Stop here. I'll continue later."

await creates a similar boundary inside an async function:

async function asyncFunction() {
  const result = await doSomething();

  continueWork(result);
}

The interesting part isn't that await is asynchronous.

The interesting part is what happens to the function itself.

When execution reaches await, that invocation of asyncFunction cannot continue yet. Its continuation is put aside until the awaited Promise settles.

JavaScript is free to do other work in the meantime.

Then the async function resumes and continues with:

continueWork(result);

So you can put the two side by side:

generator

doSomething()
     ↓
   yield
     ↓
  suspend
     ↓
  next()
     ↓
continueWork()
async function

doSomething()
     ↓
  await
     ↓
  suspend
     ↓
Promise settles
     ↓
continueWork()

The mechanisms aren't the same.

A generator is explicitly resumed with next().

An async function resumes through the Promise machinery.

But both give us the same useful idea:

a computation can suspend at a specific point and continue later from that point.

That's the connection between yield and await.

🧩 The Important Difference

It's tempting to say "await is basically yield."

That's not quite right.

Generators are manually resumable:

const generator = work();

generator.next();
generator.next();

The caller controls when the generator moves forward.

An async function works differently.

When it reaches:

await promise;

the function's continuation is tied to the Promise.

And an async function always returns a Promise:

async function work() {
  return 42;
}

work().then((value) => {
  console.log(value);
});

So await isn't simply a nicer version of yield.

The mechanisms and semantics are different.

The shared idea is what matters.

🧠 The Mental Model

If you remember only one thing, make it this:

yield
  ↓
"Pause this computation.
 Resume me when the caller decides."

await
  ↓
"Pause this computation.
 Resume me when this Promise is ready."

Both let a computation give control back instead of insisting on finishing immediately.

yield makes that behavior visible.

await hides it behind syntax that looks sequential.

That's part of why async/await feels so natural.

It lets you write:

const user = await getUser();
const posts = await getPosts(user.id);

render(user, posts);

as though the function simply runs from top to bottom.

But underneath that simple-looking code are suspension points.

The function can stop.

The rest of JavaScript can keep moving.

Then the function can pick up where it left off.

And once you've seen that idea in yield, await doesn't look quite so magical anymore.

Different mechanisms.

Same surprisingly useful idea:

a computation can suspend now and continue later.

Back to all articles

Byte Corner

Byte Corner is still growing.

Not everything needs to be practical to be worth understanding. Sometimes you just find an interesting question and follow the rabbit hole.

const curious = true;

while (curious) {

keepDigging();

}

bytecorner

© 2026 bytecorner.dev

Small reads. Big understanding.