JavaScript Iterators & Generators (yield, next, custom iterators) Quiz

JavaScript
0 Passed
0% acceptance

Answer 35 questions covering iterator protocol basics, next() behavior, iterable objects, generator syntax, yield mechanics, execution control, passing values, custom iterators, lazy generator use cases, and async-iterator differences.

35 Questions
~70 minutes
1

Question 1

What does the iterator protocol require from an object returned by Symbol.iterator?

A
It must expose a next() method that returns objects with value and done properties
B
It must be a class instance
C
It must modify the DOM
D
It must keep a length property
2

Question 2

What prints?

javascript
const iterable = {
  values: ['a', 'b'],
  [Symbol.iterator]() {
    let index = 0
    return {
      next: () => (index < this.values.length
        ? { value: this.values[index++], done: false }
        : { value: undefined, done: true })
    }
  }
}
const iterator = iterable[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
A
{ value: 'a', done: false }, { value: 'b', done: false }, { value: undefined, done: true }
B
{ value: 'a', done: false } three times
C
Throws because Symbol.iterator cannot be a method
D
{ value: undefined, done: true } three times
3

Question 3

What is the difference between an iterable and an iterator?

A
An iterable has Symbol.iterator that returns a fresh iterator, while an iterator is the object returned that implements next()
B
They are identical terms
C
Iterators only exist for arrays
D
Iterables cannot be used with for...of
4

Question 4

What prints?

javascript
const iterator = [10, 20][Symbol.iterator]()
console.log(iterator.next().value)
console.log(iterator.next().done)
console.log(iterator.next().done)
A
10, false, true
B
10, true, false
C
undefined, false, false
D
Throws after first call
5

Question 5

Why is returning { done: true } required when iteration finishes?

A
It signals consumers like for...of to stop requesting values and exit gracefully
B
It speeds up garbage collection
C
It is optional because consumers guess
D
It automatically resets the iterator
6

Question 6

How does for...of work with iterables?

A
It calls Symbol.iterator to get an iterator, then repeatedly calls next() until done becomes true
B
It copies the iterable into an array first
C
It only works for strings
D
It mutates the iterable by default
7

Question 7

What prints?

javascript
const iterator = {
  current: 1,
  next() {
    if (this.current <= 3) {
      return { value: this.current++, done: false }
    }
    return { value: undefined, done: true }
  }
}
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
A
{value:1,done:false}, {value:2,done:false}, {value:3,done:false}, {value:undefined,done:true}
B
{value:1,done:false} repeated forever
C
Throws because iterator lacks Symbol.iterator
D
Stops at second call automatically
8

Question 8

Why can iterators be stateful without being iterable?

A
The protocol only demands next(); Symbol.iterator is optional unless you need to reuse the iterator in constructs like for...of
B
All iterators must be immutable
C
State breaks the protocol
D
Iterators cannot expose next() directly
9

Question 9

What prints?

javascript
function* numbers() {
  yield 1
  yield 2
}
const iterator = numbers()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
A
{value:1,done:false}, {value:2,done:false}, {value:undefined,done:true}
B
{value:1,done:true}, {value:2,done:true}, {value:3,done:true}
C
Throws because generators cannot yield numbers
D
{value:undefined,done:true} repeated
10

Question 10

Why do generator functions use function* syntax?

A
The asterisk differentiates generator declarations from normal functions and enables the yield keyword
B
The asterisk indicates multiplication
C
Generators must return Promises
D
function* is only allowed inside classes
11

Question 11

How does yield pause execution?

A
When yield is encountered, the generator saves its current state and returns control to the caller until next() is invoked again
B
yield restarts the generator
C
yield throws an error automatically
D
yield makes the generator synchronous
12

Question 12

What prints?

javascript
function* steps() {
  yield 'load'
  return 'done'
}
const iterator = steps()
console.log(iterator.next())
console.log(iterator.next())
A
{value:'load',done:false}, {value:'done',done:true}
B
{value:'load',done:false}, {value:undefined,done:true}
C
{value:undefined,done:true} twice
D
Throws because return is illegal in generators
13

Question 13

Why should you usually ignore the final value returned from a generator?

A
Consumers rarely read the last next().value when done is true; the primary sequence comes from yielded values
B
Return always equals undefined
C
Reading it throws an error
D
for...of exposes the return value directly
14

Question 14

What happens if you call next() after done becomes true?

A
The iterator keeps returning { value: undefined, done: true }
B
It restarts the generator automatically
C
It throws TypeError
D
It returns previous values again
15

Question 15

What prints?

javascript
function* echo() {
  const first = yield 'ready'
  return `received ${first}`
}
const iterator = echo()
console.log(iterator.next())
console.log(iterator.next('data'))
A
{value:'ready',done:false}, {value:'received data',done:true}
B
{value:'ready',done:false}, {value:undefined,done:true}
C
{value:undefined,done:true} twice
D
Throws because you cannot pass values to next()
16

Question 16

How does passing a value to iterator.next(value) affect the generator?

A
The value becomes the result of the suspended yield expression, allowing bidirectional data flow
B
It resets the generator counter
C
It ignores the argument entirely
D
It forces done true immediately
17

Question 17

What does iterator.throw(error) do on a generator?

A
It injects an exception at the paused yield, letting the generator handle or rethrow it
B
It restarts iteration
C
It returns undefined silently
D
It only works on async generators
18

Question 18

What prints?

javascript
function* reader() {
  try {
    yield 'waiting'
  } catch (error) {
    return `handled ${error.message}`
  }
}
const iterator = reader()
console.log(iterator.next())
console.log(iterator.throw(new Error('oops')))
A
{value:'waiting',done:false}, {value:'handled oops',done:true}
B
{value:'waiting',done:false}, throws outside
C
{value:undefined,done:true} twice
D
Throws because generators cannot catch errors
19

Question 19

What does the yield* keyword do?

A
It delegates iteration to another iterable or generator, yielding each of its values
B
It multiplies yield values
C
It creates async generators automatically
D
It clears the iterator state
20

Question 20

What prints?

javascript
function* letters() {
  yield* ['x', 'y']
  yield 'z'
}
const iterator = letters()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
A
{value:'x',done:false}, {value:'y',done:false}, {value:'z',done:false}, {value:undefined,done:true}
B
{value:'x',done:false}, {value:'z',done:false}, {value:'y',done:false}, ...
C
Throws because yield* cannot delegate arrays
D
{value:undefined,done:true} repeated
21

Question 21

Why are generators ideal for lazy sequences?

A
They produce values on demand as next() is called, so you never allocate the entire sequence upfront
B
They precompute all values when defined
C
They mutate arrays in place
D
They require async/await
22

Question 22

How does lazy evaluation benefit performance?

A
It delays computation until needed, reducing initial latency and memory usage for large data sets
B
It guarantees O(1) time
C
It prevents any computation
D
It requires TypeScript
23

Question 23

What prints?

javascript
const iterableRange = (start, end) => ({
  [Symbol.iterator]() {
    let current = start
    return {
      next() {
        if (current <= end) {
          return { value: current++, done: false }
        }
        return { value: undefined, done: true }
      }
    }
  }
})
console.log([...iterableRange(3, 5)])
A
[3, 4, 5]
B
[5, 4, 3]
C
[]
D
Throws because custom iterators cannot be spread
24

Question 24

What role does Symbol.iterator play in custom data structures?

A
It allows you to define how your structure is iterated so for...of, spread, and destructuring operate intuitively
B
It sets default toString behavior
C
It forces mutation
D
It converts objects to arrays automatically
25

Question 25

What prints?

javascript
function* idMaker() {
  let id = 0
  while (true) {
    yield id++
  }
}
const iterator = idMaker()
console.log(iterator.next().value)
console.log(iterator.next().value)
console.log(iterator.next().value)
A
0, 1, 2
B
1, 2, 3
C
0, 0, 0
D
Throws because infinite loops are forbidden
26

Question 26

Why can generators simplify complex control flows?

A
By pausing and resuming via yield, they mimic cooperative coroutines, interleaving work with consumers
B
They block the event loop
C
They eliminate callback logic automatically
D
They remove the need for Promises
27

Question 27

How do generators differ from async generators?

A
Generators yield synchronous values; async generators yield Promises and are consumed with for await...of
B
They are identical
C
Async generators cannot use yield
D
Generators always return Promises
28

Question 28

How does for await...of interact with async generators?

A
It awaits each yielded Promise before continuing, enabling sequential consumption of async streams
B
It treats async generators as sync ones
C
It blocks until all values resolve first
D
It is invalid syntax
29

Question 29

What benefit do async generators provide over pulling from an async function repeatedly?

A
They allow consumers to iterate with for await...of, receiving values as they are produced rather than waiting for a single aggregate result
B
They always run faster
C
They eliminate the need for await
D
They automatically memoize results
30

Question 30

Why are generators handy for implementing lazy pipelines such as map/filter chains?

A
They process one element at a time only when requested, so intermediate arrays are unnecessary
B
They eagerly compute every branch
C
They run only in browsers
D
They require DOM APIs
31

Question 31

What prints?

javascript
const oddIterator = {
  value: 1,
  next() {
    const result = { value: this.value, done: false }
    this.value += 2
    if (this.value > 5) return { value: result.value, done: true }
    return result
  }
}
console.log(oddIterator.next())
console.log(oddIterator.next())
console.log(oddIterator.next())
A
{value:1,done:false}, {value:3,done:false}, {value:5,done:true} (buggy because done true still returns last value twice)
B
{value:1,done:false} repeated
C
Throws because done true cannot carry a value
D
{value:1,done:true}, {value:3,done:true}, {value:5,done:true}
32

Question 32

How can you expose the generator’s final return value to callers?

A
Call iterator.next() manually and read the value when done is true
B
Use for...of, which shows the final value automatically
C
Throw inside the generator
D
Use Symbol.return
33

Question 33

Why should custom iterators be careful when caching iterator state on the iterable itself?

A
Sharing iterator progress across multiple consumers leads to unexpected skipping or duplication
B
Symbol.iterator forbids closures
C
Iterators must be stateless
D
Caching breaks garbage collection
34

Question 34

How can generators coordinate asynchronous actions when paired with a runner?

A
Runners repeatedly call next(), await yielded Promises, and feed resolved values back in, creating linear-looking async code (pattern used by libraries like co)
B
Generators replace async/await entirely without helpers
C
Generators turn Promises into callbacks automatically
D
Generators block the event loop until completion
35

Question 35

What is a common mistake when consuming iterators manually?

A
Forgetting to check done and accidentally reading undefined values or entering infinite loops
B
Calling next() at all
C
Using destructuring with iterables
D
Using Symbol.iterator

QUIZZES IN JavaScript