Skip to main content

JavaScript Generators & Iterators

JavaScript Async Iteration

Written by Published

for await of loops over values that arrive one at a time, each behind a promise.

A normal generator yields values instantly.

An async generator can await before yielding each one.

Example

Example

javascript

async function* countSlowly(max) {
  for (let i = 1; i <= max; i++) {
    await Promise.resolve();
    yield i;
  }
}

async function run() {
  const values = [];
  for await (const n of countSlowly(3)) {
    values.push(n);
  }
  return values.join(",");
}

run().then(function (result) {
  console.log(result);
});

The output is 1,2,3, each one awaited before it arrived.

async function* and for await

async function* declares an async generator.

Inside it, both await and yield can be used.

for await (const x of source) consumes it, awaiting each value.

Syntax

Syntax

javascript

async function* source() {
  yield await getValue();
}

for await (const value of source()) {
}

for await only works inside an async function.

Each Value Is Awaited

The loop pauses for every value, not just once at the start.

Example

Example

javascript

async function* fetchEach(items) {
  for (const item of items) {
    const value = await Promise.resolve(item.toUpperCase());
    yield value;
  }
}

async function run() {
  const results = [];
  for await (const value of fetchEach(["a", "b"])) {
    results.push(value);
  }
  return results.join(",");
}

run().then(function (result) {
  console.log(result);
});

The output is A,B.

Handling a Rejected Value

An error partway through can be caught around the whole loop.

Example

Example

javascript

async function* source() {
  yield 1;
  await Promise.reject(new Error("stream failed"));
  yield 2;
}

async function run() {
  const values = [];
  try {
    for await (const n of source()) {
      values.push(n);
    }
  } catch (error) {
    values.push("caught: " + error.message);
  }
  return values.join(",");
}

run().then(function (result) {
  console.log(result);
});

The output is 1,caught: stream failed.

for await Also Accepts Plain Values

An async generator can mix immediate and awaited yields freely.

Example

Example

javascript

async function* mixed() {
  yield 1;
  yield await Promise.resolve(2);
  yield 3;
}

async function run() {
  const values = [];
  for await (const n of mixed()) {
    values.push(n);
  }
  return values.join(",");
}

run().then(function (result) {
  console.log(result);
});

The output is 1,2,3.

A Normal Generator Also Works

for await can consume a plain synchronous iterable too.

It simply awaits each value, which does nothing extra if it is not a promise.

Example

Example

javascript

function* plain() {
  yield 1;
  yield 2;
}

async function run() {
  const values = [];
  for await (const n of plain()) {
    values.push(n);
  }
  return values.join(",");
}

run().then(function (result) {
  console.log(result);
});

The output is 1,2, exactly as a plain for of would give.

Where This Is Used

Reading a network response in chunks is the classic real example.

Each chunk arrives over time, which is exactly what this loop is built for.

Example

Example

javascript

async function* chunks() {
  yield "first chunk";
  yield "second chunk";
}

async function run() {
  const parts = [];
  for await (const chunk of chunks()) {
    parts.push(chunk);
  }
  return parts.join(" + ");
}

run().then(function (result) {
  console.log(result);
});

Streaming APIs in the browser expose data in exactly this shape.

Complete Example

Complete Example

html

<!DOCTYPE html>
<html>
<head>
  <title>JavaScript Async Iteration</title>
</head>
<body>

  <h1>Async Iteration</h1>

  <p id="out">Loading...</p>

  <script>
    async function* fetchNames() {
      const names = ["Ada", "Grace", "Alan"];
      for (const name of names) {
        await new Promise(function (resolve) { setTimeout(resolve, 10); });
        yield name;
      }
    }

    async function run() {
      const found = [];
      for await (const name of fetchNames()) {
        found.push(name);
      }
      document.getElementById("out").textContent = found.join(", ");
    }

    run();
  </script>

</body>
</html>

Try It Yourself

Run the above example in the Try It Editor.

Try reporting progress:

Update #out inside the loop instead of only at the end.

Important Points

  • async function* declares an async generator.
  • Both await and yield can be used inside it.
  • for await consumes it, awaiting each value in turn.
  • An error can be caught around the whole loop with try catch.
  • for await also works on plain synchronous iterables.

Conclusion

Async iteration extends the generator pattern to values that arrive over time.

It is the natural fit for streaming data, one piece at a time.

Everything from ordinary generators still applies; only the timing changes.