|

JavaScript 36 🧬 Iterator Interface

The Iterator interface defines a standard way to access elements of a collection one at a time. It’s the mechanism that powers for...of loops, spread syntax, and destructuring in JavaScript.


A Quick Look at the Example

const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

Key idea: An iterator is an object with a next() method that returns { value, done } on each call.


What is an Iterator?

An iterator is an object that provides a standard way to traverse a sequence of values.

The Iterator Protocol

An object is an iterator if it implements a next() method that returns an object with two properties:

PropertyTypeDescription
valueanyThe current value
donebooleantrue when iteration is complete
{
    next() {
        return { value: ..., done: ... };
    }
}

The Iterable Protocol

An object is iterable if it has a Symbol.iterator method that returns an iterator.

{
    [Symbol.iterator]() {
        return {
            next() {
                return { value: ..., done: ... };
            }
        };
    }
}

Relationship:

Iterable  ──[Symbol.iterator]()──→  Iterator
                                      │
                                      └── next() → { value, done }

Built-in Iterables

Many JavaScript objects are iterable by default:

ObjectIterable?What You Get
Array✅ YesValues
String✅ YesCharacters
Map✅ Yes[key, value] pairs
Set✅ YesValues
NodeList✅ YesDOM nodes
arguments✅ YesArguments
Generator✅ YesYielded values
TypedArray✅ YesNumeric values
Plain Object {}❌ NoNot iterable

Basic Example: Manual Iteration

const arr = [10, 20, 30];
const iterator = arr[Symbol.iterator]();

let result = iterator.next();
while (!result.done) {
    console.log(result.value);
    result = iterator.next();
}
// 10
// 20
// 30

Step by step:

CallReturnsState
iterator.next(){ value: 10, done: false }Position 0 → 1
iterator.next(){ value: 20, done: false }Position 1 → 2
iterator.next(){ value: 30, done: false }Position 2 → 3
iterator.next(){ value: undefined, done: true }Finished

The for...of Loop

The for...of loop uses the iterator interface automatically.

const arr = [1, 2, 3];

for (const value of arr) {
    console.log(value);
}
// 1
// 2
// 3

What happens under the hood:

// Equivalent to:
const iterator = arr[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {
    const value = result.value;
    console.log(value);
    result = iterator.next();
}

for...of works with any iterable:

for (const char of "Hello") { }       // Characters
for (const [k, v] of new Map()) { }    // Entries
for (const v of new Set()) { }         // Values

Creating Custom Iterables

You can make your own objects iterable by implementing Symbol.iterator.

Example 1: Range Object

class Range {
    constructor(start, end) {
        this.start = start;
        this.end = end;
    }

    [Symbol.iterator]() {
        let current = this.start;
        const end = this.end;

        return {
            next() {
                if (current <= end) {
                    return { value: current++, done: false };
                }
                return { value: undefined, done: true };
            }
        };
    }
}

const range = new Range(1, 5);

for (const num of range) {
    console.log(num);
}
// 1
// 2
// 3
// 4
// 5

// Works with spread!
console.log([...new Range(1, 5)]); // [1, 2, 3, 4, 5]

// Works with destructuring!
const [first, second] = new Range(10, 20);
console.log(first, second); // 10 20

Example 2: Countdown Object

class Countdown {
    constructor(from) {
        this.from = from;
    }

    [Symbol.iterator]() {
        let current = this.from;

        return {
            next: () => {
                if (current >= 0) {
                    return { value: current--, done: false };
                }
                return { value: undefined, done: true };
            }
        };
    }
}

const countdown = new Countdown(3);

for (const num of countdown) {
    console.log(num);
}
// 3
// 2
// 1
// 0

Note: The arrow function preserves this — useful in iterators.


Example 3: Fibonacci Sequence

class Fibonacci {
    constructor(limit) {
        this.limit = limit;
    }

    [Symbol.iterator]() {
        let a = 0, b = 1, count = 0;
        const limit = this.limit;

        return {
            next() {
                if (count >= limit) {
                    return { value: undefined, done: true };
                }
                const value = a;
                [a, b] = [b, a + b];
                count++;
                return { value, done: false };
            }
        };
    }
}

const fib = new Fibonacci(8);
console.log([...fib]); // [0, 1, 1, 2, 3, 5, 8, 13]

Generators — A Simpler Way

Generators are functions that return iterators automatically. They’re a much cleaner way to create iterables.

function* range(start, end) {
    for (let i = start; i <= end; i++) {
        yield i;
    }
}

console.log([...range(1, 5)]); // [1, 2, 3, 4, 5]

for (const num of range(10, 13)) {
    console.log(num);
}
// 10
// 11
// 12
// 13

Generator versions of the earlier examples:

// Range as generator
function* range(start, end) {
    for (let i = start; i <= end; i++) {
        yield i;
    }
}

// Countdown as generator
function* countdown(from) {
    for (let i = from; i >= 0; i--) {
        yield i;
    }
}

// Fibonacci as generator
function* fibonacci(limit) {
    let a = 0, b = 1;
    for (let i = 0; i < limit; i++) {
        yield a;
        [a, b] = [b, a + b];
    }
}

Key insight: Generators implement Symbol.iterator automatically — you don’t have to write next() yourself.


Iterators Are Stateful (One-Time Use)

An iterator remembers its position and can only be consumed once.

const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }

// If you save the iterator and come back to it:
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

// Once done, it stays done:
console.log(iterator.next()); // { value: undefined, done: true }

Iterable vs Iterator:

ConceptDescriptionCan Iterate Multiple Times?
IterableHas Symbol.iterator method✅ Yes (each call returns a new iterator)
IteratorHas next() method❌ No (stateful, one-time use)
const arr = [1, 2, 3];      // Iterable — can iterate many times
const iter = arr[Symbol.iterator](); // Iterator — consumed once

for (const x of arr) { }    // ✅ First iteration — works
for (const x of arr) { }    // ✅ Second iteration — still works

for (const x of iter) { }   // ✅ First iteration — works
for (const x of iter) { }   // ❌ Second iteration — empty! (exhausted)

Where Iterators Are Used

FeatureUses Iterator?
for...of loop✅ Yes
Spread [...arr]✅ Yes
Destructuring [a, b] = arr✅ Yes
Array.from(iterable)✅ Yes
Promise.all(iterable)✅ Yes
new Map(iterable)✅ Yes
new Set(iterable)✅ Yes
yield* in generators✅ Yes

Complete Example

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Iterator Interface</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background: #f8f9fa;
            color: #333;
            line-height: 1.6;
        }
        h1 { color: #007bff; border-bottom: 3px solid #007bff; padding-bottom: 10px; }
        h2 { color: #28a745; border-left: 4px solid #28a745; padding-left: 15px; margin-top: 30px; }
        .demo-box {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            margin: 15px 0;
        }
        pre {
            background: #1e1e1e;
            color: #d4d4d4;
            padding: 15px;
            border-radius: 8px;
            overflow-x: auto;
            font-family: 'Courier New', monospace;
            font-size: 0.9rem;
            line-height: 1.8;
        }
        .keyword { color: #569cd6; }
        .string { color: #ce9178; }
        .number { color: #b5cea8; }
        .function { color: #dcdcaa; }
        .comment { color: #6a9955; }
        .boolean { color: #569cd6; }
        .class-name { color: #4ec9b0; }
        #output {
            background: #e9ecef;
            padding: 15px;
            border-radius: 8px;
            margin-top: 15px;
            min-height: 40px;
            font-family: 'Courier New', monospace;
            font-size: 0.85rem;
            border-left: 4px solid #007bff;
            white-space: pre-wrap;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin: 15px 0;
        }
        th, td {
            padding: 10px;
            border: 1px solid #ddd;
            text-align: left;
        }
        th { background: #007bff; color: white; }
        tr:nth-child(even) { background: #f8f9fa; }
        .btn {
            padding: 10px 20px;
            background: #007bff;
            color: white;
            border: none;
            border-radius: 6px;
            font-size: 1em;
            font-weight: bold;
            cursor: pointer;
            margin: 5px;
            transition: all 0.3s;
        }
        .btn:hover {
            background: #0056b3;
            transform: translateY(-2px);
        }
        .btn-success { background: #28a745; }
        .btn-success:hover { background: #1e7e34; }
        .class-display {
            font-family: 'Courier New', monospace;
            font-size: 1em;
            background: #f8f9fa;
            padding: 15px;
            border-radius: 8px;
            margin: 10px 0;
            border-left: 4px solid #007bff;
            white-space: pre-wrap;
            line-height: 1.8;
        }
        .input-group {
            margin: 10px 0;
        }
        .input-group label {
            display: inline-block;
            min-width: 120px;
            font-weight: bold;
        }
        .input-group input {
            padding: 8px 12px;
            border: 2px solid #ddd;
            border-radius: 6px;
            font-size: 1em;
            width: 150px;
        }
        .input-group input:focus {
            outline: none;
            border-color: #007bff;
        }
    </style>
</head>
<body>

    <h1>Iterator Interface</h1>

    <div class="demo-box">
        <h2>1. Manual Iteration</h2>
        <pre>
<span class="keyword">const</span> arr = [<span class="number">10</span>, <span class="number">20</span>, <span class="number">30</span>];
<span class="keyword">const</span> iterator = arr[<span class="function">Symbol.iterator</span>]();

console.log(iterator.<span class="function">next</span>()); <span class="comment">// { value: 10, done: false }</span>
console.log(iterator.<span class="function">next</span>()); <span class="comment">// { value: 20, done: false }</span>
console.log(iterator.<span class="function">next</span>()); <span class="comment">// { value: 30, done: false }</span>
console.log(iterator.<span class="function">next</span>()); <span class="comment">// { value: undefined, done: true }</span>
        </pre>
    </div>

    <div class="demo-box">
        <h2>2. Iterator Protocol</h2>
        <pre>
<span class="keyword">const</span> myIterator = {
    next() {
        <span class="keyword">return</span> { value: <span class="number">1</span>, done: <span class="boolean">false</span> };
    }
};

<span class="keyword">const</span> myIterable = {
    [<span class="function">Symbol.iterator</span>]() {
        <span class="keyword">return</span> myIterator;
    }
};
        </pre>
    </div>

    <div class="demo-box">
        <h2>3. Built-in Iterables</h2>
        <table>
            <tr>
                <th>Object</th>
                <th>Iterable?</th>
                <th>Yields</th>
            </tr>
            <tr><td>Array</td><td>✅</td><td>Values</td></tr>
            <tr><td>String</td><td>✅</td><td>Characters</td></tr>
            <tr><td>Map</td><td>✅</td><td>[key, value] pairs</td></tr>
            <tr><td>Set</td><td>✅</td><td>Values</td></tr>
            <tr><td>NodeList</td><td>✅</td><td>DOM nodes</td></tr>
            <tr><td>arguments</td><td>✅</td><td>Arguments</td></tr>
            <tr><td>Generator</td><td>✅</td><td>Yielded values</td></tr>
            <tr><td>Plain Object {}</td><td>❌</td><td>—</td></tr>
        </table>
    </div>

    <div class="demo-box">
        <h2>4. Interactive: Custom Range Iterator</h2>
        <div class="input-group">
            <label for="startInput">Start:</label>
            <input type="number" id="startInput" value="1">
        </div>
        <div class="input-group">
            <label for="endInput">End:</label>
            <input type="number" id="endInput" value="5">
        </div>
        <div style="margin: 10px 0;">
            <button class="btn btn-success" onclick="iterateRange()">Iterate (for...of)</button>
            <button class="btn" onclick="spreadRange()">Spread [...range]</button>
            <button class="btn" onclick="destructureRange()">Destructure [a, b]</button>
            <button class="btn" onclick="manualIterate()">Manual next()</button>
        </div>
        <div class="class-display" id="rangeDisplay">Click a button to test the Range iterator</div>
    </div>

    <div class="demo-box">
        <h2>5. Interactive: Fibonacci Generator</h2>
        <div class="input-group">
            <label for="fibCount">How many?</label>
            <input type="number" id="fibCount" value="10" min="1" max="30">
        </div>
        <div style="margin: 10px 0;">
            <button class="btn btn-success" onclick="generateFibonacci()">Generate</button>
            <button class="btn" onclick="fibToArray()">As Array</button>
        </div>
        <div class="class-display" id="fibDisplay">Click "Generate" to see Fibonacci numbers</div>
    </div>

    <div class="demo-box">
        <h2>6. Live Output — All Examples</h2>
        <div id="output">Loading...</div>
    </div>

    <script>
        // ============================================
        // Iterator Interface — Live Demo
        // ============================================

        let results = [];

        // 1. Manual iteration
        results.push('📌 Manual Iteration:\n');

        const arr = [10, 20, 30];
        const iterator = arr[Symbol.iterator]();

        results.push('  const iterator = [10, 20, 30][Symbol.iterator]()');
        results.push('  iterator.next() → ' + JSON.stringify(iterator.next()));
        results.push('  iterator.next() → ' + JSON.stringify(iterator.next()));
        results.push('  iterator.next() → ' + JSON.stringify(iterator.next()));
        results.push('  iterator.next() → ' + JSON.stringify(iterator.next()));
        results.push('  → After done: true, the iterator is exhausted');
        results.push('');

        // 2. Iterating through different structures
        results.push('📌 Iterating Different Structures:\n');

        // Array
        let arrValues = [];
        for (const v of [1, 2, 3]) arrValues.push(v);
        results.push('  Array:   [1, 2, 3] → [' + arrValues.join(', ') + ']');

        // String
        let strChars = [];
        for (const c of "abc") strChars.push(c);
        results.push('  String:  "abc" → [' + strChars.join(', ') + ']');

        // Map
        let mapEntries = [];
        for (const [k, v] of new Map([['a', 1], ['b', 2]])) {
            mapEntries.push(`${k}=${v}`);
        }
        results.push('  Map:     Map([a→1, b→2]) → [' + mapEntries.join(', ') + ']');

        // Set
        let setValues = [];
        for (const v of new Set([1, 2, 2, 3, 3])) setValues.push(v);
        results.push('  Set:     Set([1, 2, 2, 3, 3]) → [' + setValues.join(', ') + '] (duplicates removed)');
        results.push('');

        // 3. Custom iterable — Range
        results.push('📌 Custom Iterable — Range:\n');

        class Range {
            constructor(start, end) {
                this.start = start;
                this.end = end;
            }

            [Symbol.iterator]() {
                let current = this.start;
                const end = this.end;

                return {
                    next() {
                        if (current <= end) {
                            return { value: current++, done: false };
                        }
                        return { value: undefined, done: true };
                    }
                };
            }
        }

        const range = new Range(1, 5);
        results.push('  const range = new Range(1, 5)');

        let rangeValues = [];
        for (const n of range) rangeValues.push(n);
        results.push('  for...of: [...range] → [' + rangeValues.join(', ') + ']');
        results.push('  Spread:   [...range] → [' + [...range].join(', ') + ']');

        const [r1, r2, r3] = new Range(10, 20);
        results.push('  Destructure: const [a, b, c] = new Range(10, 20)');
        results.push('    → a=' + r1 + ', b=' + r2 + ', c=' + r3);
        results.push('');

        // 4. Custom iterable — Countdown
        results.push('📌 Custom Iterable — Countdown:\n');

        class Countdown {
            constructor(from) {
                this.from = from;
            }

            [Symbol.iterator]() {
                let current = this.from;
                return {
                    next: () => {
                        if (current >= 0) {
                            return { value: current--, done: false };
                        }
                        return { value: undefined, done: true };
                    }
                };
            }
        }

        const countdown = new Countdown(3);
        results.push('  const countdown = new Countdown(3)');
        results.push('  [...countdown] → [' + [...countdown].join(', ') + ']');
        results.push('');

        // 5. Generator version — much simpler!
        results.push('📌 Generator Version (Simpler):\n');

        function* rangeGen(start, end) {
            for (let i = start; i <= end; i++) {
                yield i;
            }
        }

        results.push('  function* rangeGen(start, end) {');
        results.push('      for (let i = start; i <= end; i++) yield i;');
        results.push('  }');
        results.push('  [...rangeGen(1, 5)] → [' + [...rangeGen(1, 5)].join(', ') + ']');
        results.push('  → Generators implement Symbol.iterator automatically!');
        results.push('');

        // 6. Fibonacci generator
        results.push('📌 Fibonacci Generator:\n');

        function* fibonacci(limit) {
            let a = 0, b = 1;
            for (let i = 0; i < limit; i++) {
                yield a;
                [a, b] = [b, a + b];
            }
        }

        results.push('  function* fibonacci(limit) {');
        results.push('      let a = 0, b = 1;');
        results.push('      for (let i = 0; i < limit; i++) {');
        results.push('          yield a;');
        results.push('          [a, b] = [b, a + b];');
        results.push('      }');
        results.push('  }');
        results.push('');
        results.push('  [...fibonacci(10)] → [' + [...fibonacci(10)].join(', ') + ']');
        results.push('');

        // 7. Iterator is one-time use
        results.push('📌 Iterator vs Iterable:\n');

        const arr2 = [1, 2, 3];
        const iter = arr2[Symbol.iterator]();

        results.push('  const arr = [1, 2, 3] (Iterable)');
        results.push('  const iter = arr[Symbol.iterator]() (Iterator)');
        results.push('');

        let firstIter = [];
        for (const x of iter) firstIter.push(x);
        results.push('  First iteration of iter: [' + firstIter.join(', ') + ']');

        let secondIter = [];
        for (const x of iter) secondIter.push(x);
        results.push('  Second iteration of iter: [' + secondIter.join(', ') + '] (empty — exhausted!)');
        results.push('');

        let arrIter1 = [];
        for (const x of arr2) arrIter1.push(x);
        let arrIter2 = [];
        for (const x of arr2) arrIter2.push(x);
        results.push('  First iteration of arr:  [' + arrIter1.join(', ') + ']');
        results.push('  Second iteration of arr: [' + arrIter2.join(', ') + '] (works — new iterator each time!)');
        results.push('');

        // 8. Where iterators are used
        results.push('📌 Where Iterators Are Used:\n');

        const numRange = rangeGen(1, 5);

        results.push('  for...of          → uses Symbol.iterator');
        results.push('  Spread [...x]     → uses Symbol.iterator');
        results.push('  Destructuring     → uses Symbol.iterator');
        results.push('  Array.from(x)     → uses Symbol.iterator');
        results.push('  new Map(x)        → uses Symbol.iterator');
        results.push('  new Set(x)        → uses Symbol.iterator');
        results.push('  Promise.all(x)    → uses Symbol.iterator');
        results.push('  yield* in generator → uses Symbol.iterator');
        results.push('');

        // 9. Practical: custom iterable collection
        results.push('📌 Practical: Iterable Playlist:\n');

        class Playlist {
            constructor(name, songs) {
                this.name = name;
                this.songs = songs;
            }

            [Symbol.iterator]() {
                let index = 0;
                const songs = this.songs;

                return {
                    next() {
                        if (index < songs.length) {
                            return { value: songs[index++], done: false };
                        }
                        return { value: undefined, done: true };
                    }
                };
            }

            // Bonus: use generator for cleaner code
            *[Symbol.iterator]() {
                for (const song of this.songs) {
                    yield song;
                }
            }
        }

        const playlist = new Playlist('Road Trip', [
            'Highway to Hell',
            'Born to Run',
            'Don\'t Stop Believin\''
        ]);

        results.push('  const playlist = new Playlist("Road Trip", [...])');
        results.push('  Iterating through the playlist:');

        for (const song of playlist) {
            results.push('    🎵 ' + song);
        }

        results.push('');
        results.push('  Spread: [...playlist] → ' + JSON.stringify([...playlist]));
        results.push('  Count:  ' + [...playlist].length + ' songs');

        document.getElementById('output').textContent = results.join('\n');

        // ============================================
        // Interactive: Range Iterator
        // ============================================

        function getRangeInputs() {
            return {
                start: Number(document.getElementById('startInput').value),
                end: Number(document.getElementById('endInput').value)
            };
        }

        function iterateRange() {
            const { start, end } = getRangeInputs();
            const range = new Range(start, end);
            const values = [];
            for (const n of range) values.push(n);

            document.getElementById('rangeDisplay').textContent =
                `for...of Iteration:\n` +
                `─────────────────────\n` +
                `Range(${start}, ${end})\n` +
                `\n` +
                `Values: [${values.join(', ')}]\n` +
                `Count: ${values.length}`;
        }

        function spreadRange() {
            const { start, end } = getRangeInputs();
            const range = new Range(start, end);
            const values = [...range];

            document.getElementById('rangeDisplay').textContent =
                `Spread Operator:\n` +
                `─────────────────────\n` +
                `[...new Range(${start}, ${end})]\n` +
                `\n` +
                `Result: [${values.join(', ')}]\n` +
                `\n` +
                `→ Spread uses Symbol.iterator automatically!`;
        }

        function destructureRange() {
            const { start, end } = getRangeInputs();
            const [a, b, c, ...rest] = new Range(start, end);

            document.getElementById('rangeDisplay').textContent =
                `Destructuring:\n` +
                `─────────────────────\n` +
                `const [a, b, c, ...rest] = new Range(${start}, ${end})\n` +
                `\n` +
                `a = ${a}\n` +
                `b = ${b}\n` +
                `c = ${c}\n` +
                `rest = [${rest.join(', ')}]\n` +
                `\n` +
                `→ Destructuring uses Symbol.iterator!`;
        }

        function manualIterate() {
            const { start, end } = getRangeInputs();
            const iterator = new Range(start, end)[Symbol.iterator]();

            let text = `Manual next() Calls:\n`;
            text += `─────────────────────\n`;

            for (let i = 0; i < 4; i++) {
                const result = iterator.next();
                text += `iterator.next() → ${JSON.stringify(result)}\n`;
            }
            text += `\n→ After done: true, iteration is complete`;

            document.getElementById('rangeDisplay').textContent = text;
        }

        // ============================================
        // Interactive: Fibonacci Generator
        // ============================================

        function* fibGen(limit) {
            let a = 0, b = 1;
            for (let i = 0; i < limit; i++) {
                yield a;
                [a, b] = [b, a + b];
            }
        }

        function generateFibonacci() {
            const count = Number(document.getElementById('fibCount').value);
            const display = document.getElementById('fibDisplay');

            let text = `Fibonacci Sequence (${count} numbers):\n`;
            text += `─────────────────────\n`;

            let i = 1;
            for (const n of fibGen(count)) {
                text += `${i}. ${n}\n`;
                i++;
            }

            display.textContent = text;
        }

        function fibToArray() {
            const count = Number(document.getElementById('fibCount').value);
            const values = [...fibGen(count)];

            document.getElementById('fibDisplay').textContent =
                `Fibonacci as Array:\n` +
                `─────────────────────\n` +
                `const fib = [...fibGen(${count})]\n` +
                `\n` +
                `[${values.join(', ')}]\n` +
                `\n` +
                `Length: ${values.length}\n` +
                `Sum: ${values.reduce((a, b) => a + b, 0)}`;
        }
    </script>

</body>
</html>

Quick Reference

Iterator Protocol

RequirementDescription
next() methodReturns { value, done }
valueCurrent value
donetrue when finished

Iterable Protocol

RequirementDescription
Symbol.iterator methodReturns an iterator
Called once per iterationEach for...of gets a fresh iterator

Built-in Iterables

ObjectYields
ArrayValues
StringCharacters
Map[key, value] pairs
SetUnique values
NodeListDOM nodes
argumentsArguments
GeneratorYielded values
TypedArrayNumbers
Plain Object❌ Not iterable

Where Iterators Are Used

FeatureExample
for...offor (const x of arr)
Spread[...arr]
Destructuringconst [a, b] = arr
Array.from()Array.from(iterable)
new Map() / new Set()new Set(iterable)
Promise.all()Promise.all(iterable)
yield*yield* iterable

Best Practices

Do This:

// Use generators for clean, readable iterables
function* range(start, end) {
    for (let i = start; i <= end; i++) yield i;
}

// Implement Symbol.iterator for custom objects
class MyCollection {
    [Symbol.iterator]() { /* ... */ }
}

// Return a fresh iterator each time
[Symbol.iterator]() {
    let index = 0;
    return { next() { /* ... */ } };
}

// Use iterables with array methods
const doubled = [...range(1, 5)].map(n => n * 2);

// Prefer generators over manual iterators
// Generators are much shorter and less error-prone

Don’t Do This:

// Don't reuse iterators — they're one-time use
const iter = arr[Symbol.iterator]();
for (const x of iter) { }  // ✅ Works
for (const x of iter) { }  // ❌ Empty — exhausted!

// Don't forget to return { done: true } eventually
// → Infinite loops!
next() {
    return { value: 1, done: false }; // ❌ Never done!
}

// Don't iterate plain objects directly
const obj = { a: 1 };
for (const x of obj) { }  // ❌ TypeError: obj is not iterable
// Use: for (const [k, v] of Object.entries(obj))

// Don't confuse iterable with iterator
// Iterable → has Symbol.iterator, can iterate many times
// Iterator → has next(), can only be consumed once

Common Pitfalls

PitfallProblemSolution
Reusing an iteratorSecond iteration is emptyCall Symbol.iterator again
Iterating plain objectsTypeErrorUse Object.entries/keys/values
Forgetting done: trueInfinite loopAlways return done eventually
Confusing iterable/iteratorWrong assumptionsIterable → can iterate many times
Not using generatorsVerbose iteratorsUse function* for simplicity

Iterable vs Iterator — Visual

┌─────────────────────────────────────┐
│         ITERABLE (Array)            │
│                                     │
│  [1, 2, 3]                          │
│  └── Symbol.iterator() ────┐        │
└────────────────────────────┼────────┘
                             │
                             ▼
              ┌───────────────────────────┐
              │     ITERATOR              │
              │  ┌─────────────────────┐  │
              │  │ next() → { value }  │  │
              │  └─────────────────────┘  │
              └───────────────────────────┘

Iterable: Has Symbol.iterator → can iterate many times
Iterator: Has next() → stateful, consumed once

When to Use What

GoalUse
Simple sequenceGenerator function
Custom collectionSymbol.iterator method
Lazy evaluationGenerator with yield
Infinite sequencesGenerator (never done)
Reverse iterationCustom iterator
Multiple values at onceGenerator with yield*

Pro Tip: The iterator interface is one of the most powerful features of modern JavaScript. It’s the unifying protocol behind for...of, spread, destructuring, and many built-in methods.

Key points:

  • Iterator = object with next() → returns { value, done }
  • Iterable = object with Symbol.iterator → returns an iterator
  • for...of uses iterators automatically
  • Generators (function*) implement iterators for free
  • Custom objects can become iterable by implementing Symbol.iterator

When to use:

  • Make your own collections iterable for for...of
  • Use generators for lazy sequences (compute on demand)
  • Use generators for infinite sequences
  • Use Symbol.iterator for custom traversal logic

The golden rule: If you want your object to work with for...of, spread, or destructuring — implement Symbol.iterator. And if you just need a quick iterable, use a generator — it’s almost always simpler than writing an iterator manually!


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!