Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function generators in for loops

I'm trying to have a better understanding of how generators work in javascript.

From MDN:

The function* declaration (function keyword followed by an asterisk) defines a generator function, which returns a Generator object.

function *range(from, to) {
    var counter = from;
    while(to >= counter) {
        yield counter
        counter++
    }        
}

for (var r of range(5, 10)) {
    console.log( r );
}

// print: 5, 6, 7, 8, 9, 10

I'm not sure how I understand exactly what's happening in the snippet above.

Aren't generators supposed to be called, stored as (generator) objects and then called by the next() method. (like below)

function *foo () {
      yield 'woo';
 }
 var G = foo();
 console.log( G.next() );

In the code above, on line 4 with var G = foo(); I am not calling a function and creating new execution context, this should just return a generator object (and store it under the label G).

I'll invoke the actual function foo, when I call the next() method on it, on line 5. At that point, I'm creating an execution context, executing the code inside foo and yielding out the string "woo".

How the first snippet is exactly supposed to work?

like image 962
leonardofed Avatar asked Sep 06 '25 10:09

leonardofed


1 Answers

Calling a generator function will return an iterator (the object with a .next function), and a for..of loop will automatically iterate over iterable objects. While you can store the iterator in a variable beforehand:

const iter = range(5, 10);
for (var r of iter) {
  ...
}

it's not necessary - the for..of only needs a single reference to an iterator, after all.

You could imitate this in code by passing a single reference to an iterator into a function that calls each .next function until the iterator is exhausted:

function *range(from, to) {
    var counter = from;
    while(to >= counter) {
        yield counter
        counter++
    }        
}

iterate(range(5, 10), num => {
  console.log(num);
});

function iterate(iterator, callback) {
  while (true) {
    const { value, done } = iterator.next();
    if (done) return;
    callback(value);
  }
}

As you can see, there's no need to store the iterator in a variable before passing it to iterate, just like you can use the range(5, 10) call directly to a for..of loop, because the loop (or function)'s internals do all the iteration for you.

like image 106
CertainPerformance Avatar answered Sep 08 '25 18:09

CertainPerformance