Lesson 39: Mastering JavaScript Iterable with challenges!

manoj ymkmanoj ymk
5 min read

What is an Iterable?

An iterable is any object in JavaScript that can be used in a for..of loop. It follows a specific protocol to allow iteration over its values.

Protocol:

To be iterable, an object must implement the Symbol.iterator method, which returns an iterator.

Iterator:

An iterator is an object that provides a next() method which returns an object of the form:

jsCopyEdit{ value: ..., done: true/false }

Built-in Iterables:

  • Strings

  • Arrays

  • Maps

  • Sets

  • Typed Arrays

  • Many DOM collections

Key Use Case:

jsCopyEditfor (let item of iterable) {
  // Automatically uses iterable[Symbol.iterator]()
}

βš™οΈ 2. Internal Mechanics

Step-by-step: How for..of Works Internally

  1. Calls the object's [Symbol.iterator]() method.

  2. That returns an iterator object.

  3. Then for..of repeatedly calls iterator.next() until { done: true }.

Example:

jsCopyEditlet range = {
  from: 1,
  to: 5,
  [Symbol.iterator]() {
    this.current = this.from;
    return this;
  },
  next() {
    if (this.current <= this.to) {
      return { done: false, value: this.current++ };
    } else {
      return { done: true };
    }
  }
};

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

πŸ•΅οΈβ€β™‚οΈ 3. Hidden Quirks & Pitfalls

1. No simultaneous iteration if object is its own iterator:

jsCopyEditlet iter = range[Symbol.iterator](); 
let iter2 = range[Symbol.iterator](); 
console.log(iter.next()); // Works
console.log(iter2.next()); // Same shared state β€” may be broken or overlap

2. Infinite Iterators: Dangerous in for..of!

jsCopyEditlet infinite = {
  [Symbol.iterator]() {
    return {
      next() {
        return { done: false, value: Math.random() };
      }
    };
  }
};
// for (let val of infinite) break; // would run forever without break

🟒 Basic

  1. Iterate Over a String
    Create a loop that prints each character in the string "Mastery" using for...of. Don’t use .split().

  2. Manual Iterator from Array
    Manually extract and log the first 3 elements from an array using its [Symbol.iterator] directly.

  3. Array-like to Iterable
    Convert an array-like object (with numeric keys and length) into an iterable object so it can be used in a for...of loop.


🟑 Intermediate

  1. Custom Range Iterable
    Build an object that behaves like range(3, 7) and supports for...of, yielding numbers from 3 to 7.

  2. Pauseable Iterator
    Create a custom iterable that yields 5 numbers, but allows external control to pause and resume iteration.

  3. Filter During Iteration
    Create an iterable that filters out odd numbers during iteration (only even numbers should be yielded from a given array).

  4. Iterable Reverse Proxy
    Make a string iterable that yields characters in reverse order via a custom iterator.


πŸ”΄ Advanced

  1. Composable Iterables
    Create a function combine(iter1, iter2) that merges two iterable sequences into one (like zip or concat).

  2. Memoized Iterator
    Build an iterator that remembers all previously yielded values and allows "replay" of iteration from the start.

  3. Async Iterable Bridge
    Create a sync iterable wrapper over an async generator (simulate paginated API results in sync for...of).


🎯 Bonus Brain-Twister

  1. Two-way Iterator Trap
    Create a custom iterable where:
  • Forward loop yields characters in order.

  • Reverse loop (when used with a flag) yields characters in reverse.

  • You must use only one [Symbol.iterator]() but behave differently depending on the flag.


πŸ”Ή 4. Interview-Ready Questions

Mix of conceptual, scenario-based, and debugging-style questions. Covers real-world cases and common pitfalls.


πŸ“˜ Conceptual Questions

  1. What is the difference between an iterable and an iterator in JavaScript?

  2. Why can't we use for...of on all objects? How can we make a plain object iterable?

  3. How does JavaScript's for...of loop internally know when to stop?


πŸ”„ Scenario-Based Questions

  1. You have an object with numeric keys and a length property, but for...of throws a TypeError. What could be the reason?

  2. You need to iterate over DOM NodeList elements β€” how do you convert them to a real array and explain why that might be helpful?

  3. You're reading a paginated API and want to load each page lazily. How can you design a custom iterable to handle that?


🐞 Debugging-Style Questions

  1. You created a custom iterable, but it only yields one value and stops. What might be wrong in your next() implementation?

  2. Your for...of loop seems to be stuck in an infinite loop β€” what likely went wrong in your custom iterator?

  3. You define [Symbol.iterator] on an object but for...of still doesn't work. What could be missing?

  4. You’re using Array.from(customIterable) but get an empty array β€” what should you inspect?

🌍 5. Real-World Usage

βœ… Looping over:

  • DOM NodeLists

  • Custom data ranges

  • Paginated APIs (e.g., via async iterators)

βœ… Use Array.from() to:

  • Convert NodeList β†’ Array

  • Convert Set β†’ Array

  • Apply mapping during conversion

jsCopyEditArray.from("abc", c => c.toUpperCase()); // ["A", "B", "C"]

6.Mind Map (Text Version)

typescriptCopyEditIterable
β”‚
β”œβ”€β”€ [Symbol.iterator] method
β”‚   └── Returns an Iterator object
β”‚        └── next() β†’ {value, done}
β”‚
β”œβ”€β”€ Built-in Iterables
β”‚   β”œβ”€β”€ String
β”‚   β”œβ”€β”€ Array
β”‚   └── Map, Set, TypedArray, etc.
β”‚
β”œβ”€β”€ Custom Iterables
β”‚   └── Implement [Symbol.iterator]
β”‚        └── Return iterator with next()
β”‚
β”œβ”€β”€ Array-like vs Iterable
β”‚   β”œβ”€β”€ Array-like β†’ { 0: ..., length: ... }
β”‚   └── Iterable β†’ has Symbol.iterator
β”‚
└── Utilities
    └── Array.from()
         β”œβ”€β”€ Converts iterable/array-like to Array
         └── Optional mapping function

πŸŽ‰ 7. Fun Application

✨ Build a blinking text animation using a custom iterable:

jsCopyEditlet textBlinker = {
  text: "πŸ’‘JavaScriptπŸ’‘",
  [Symbol.iterator]() {
    let visible = true;
    return {
      next: () => ({
        done: false,
        value: visible ? this.text : " ".repeat(this.text.length),
        toggle: () => visible = !visible
      })
    };
  }
};

let iter = textBlinker[Symbol.iterator]();
setInterval(() => {
  let result = iter.next();
  console.clear();
  console.log(result.value);
  result.toggle();
}, 500);
0
Subscribe to my newsletter

Read articles from manoj ymk directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

manoj ymk
manoj ymk