JavaScript
iteration
synchronous iterators
chaining
programming tips

How to sequentially chain two sync iterators in JavaScript?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

If you want to iterate through one synchronous iterable and then continue with another, the simplest JavaScript answer is to use a generator. A chained generator stays lazy, works with for...of, and avoids copying values into a temporary array.

The Easiest Pattern: yield*

Generators are a natural fit because yield* delegates iteration to another iterable. To chain two iterables in sequence, yield from the first one and then from the second one.

javascript
1function* chain(iterableA, iterableB) {
2  yield* iterableA;
3  yield* iterableB;
4}
5
6const first = [1, 2, 3];
7const second = ["a", "b"];
8
9for (const value of chain(first, second)) {
10  console.log(value);
11}

The output is 1, 2, 3, a, b in that order. Nothing is eagerly combined. The next value is produced only when the consumer asks for it.

Why This Works for Sync Iterators

Any object that implements Symbol.iterator is iterable. Arrays, sets, strings, and generator results all fit this model. yield* simply forwards control to the next iterable until it is exhausted, then your generator continues with the following one.

That means the chaining function can stay generic. It does not need to know whether the input came from an array, a custom iterator, or another generator.

A Manual Iterator Version

If you want to see the mechanics more directly, you can implement the same behavior with an object that exposes next() and Symbol.iterator.

javascript
1function chainManual(iterableA, iterableB) {
2  const iterators = [iterableA[Symbol.iterator](), iterableB[Symbol.iterator]()];
3  let index = 0;
4
5  return {
6    [Symbol.iterator]() {
7      return this;
8    },
9    next() {
10      while (index < iterators.length) {
11        const result = iterators[index].next();
12        if (!result.done) {
13          return result;
14        }
15        index += 1;
16      }
17      return { value: undefined, done: true };
18    }
19  };
20}
21
22for (const value of chainManual([1, 2], [3, 4])) {
23  console.log(value);
24}

This version is more verbose, but it helps explain the underlying rule: consume the first iterator until it finishes, then switch to the second one.

Why Not Just Concatenate Arrays

If your inputs are definitely arrays, [...a, ...b] or a.concat(b) is perfectly fine. But those approaches create a new array immediately. Chaining iterators is more general and preserves lazy evaluation, which matters when values are expensive to produce or potentially large.

That is why generator-based chaining is often the best default for iterator-oriented code.

Iterables Versus Iterator Objects

Most examples use arrays and generators, which are iterables. If you already have raw iterator objects, wrap them or adapt the chaining function accordingly. The important rule is that the consumer needs a valid iterator protocol, and yield* works best when the input is iterable rather than just a bare object with next(). That makes the helper easier to reuse safely.

Common Pitfalls

  • 'yield* expects an iterable, so passing a plain object without Symbol.iterator will fail.'
  • Chaining iterators is lazy, which is usually good, but it also means side effects happen only during consumption.
  • If the first iterable throws during iteration, the second one is never reached.
  • Array concatenation is simpler for small array-only cases, but it is not the same thing as iterator chaining.

Summary

  • The simplest way to chain two synchronous iterables in JavaScript is a generator with yield*.
  • That approach stays lazy and works with any iterable, not just arrays.
  • A manual next() implementation can do the same thing, but it is more verbose.
  • Use array concatenation only when eager materialization is acceptable and the inputs are definitely arrays.

Course illustration
Course illustration

All Rights Reserved.