1

Consider this generator function.

Why is the argument of the first call to .next() essentially lost? It will yield and log each of the letter strings but skips "A". Can someone offer an explanation. Please advise on a way that I can access each argument each argument of the .next() method and store it in an array within the generator function?

function* gen(arg) {
  let argumentsPassedIn = [];
  while (true) {
    console.log(argumentsPassedIn);
    arg = yield arg;
    argumentsPassedIn.push(arg);
  }

}


const g = gen();
g.next("A"); // ??
g.next("B");
g.next("C");
g.next("D");
g.next("E");
WillD
  • 5,170
  • 6
  • 27
  • 56
  • From [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*#passing_arguments_into_generators): "the first call of next executes from the start of the function until the first yield statement" – Heretic Monkey Aug 25 '22 at 18:40

3 Answers3

3

As per the docs

The first call of next executes from the start of the function until the first yield statement

So when you call the first next, it just calls the generator function from start to till the first yield and then returns and from next call it works normal.

To make you code work, you should try like this.

function* gen(arg) {
  let argumentsPassedIn = [];
  while (true) {
    console.log(argumentsPassedIn);
    arg = yield arg;
    argumentsPassedIn.push(arg);
  }

}


const g = gen();
g.next()
g.next("A"); // ??
g.next("B");
g.next("C");
g.next("D");
g.next("E");
Naren
  • 4,152
  • 3
  • 17
  • 28
  • Not quite. `arg` will be "A" in that first call. `argumentsPassedIn` will be empty (since it is declared as an empty array), logged as empty, then "A" will be yielded and the function completes (without pushing). – Heretic Monkey Aug 25 '22 at 18:45
  • Yeah, bit confusing. – Naren Aug 25 '22 at 18:48
  • If so, the question is, Why isn't there an empty `arg` in the array? – Naren Aug 25 '22 at 18:50
  • Why would there be? As I said, nothing was pushed. From the very sentence you quote: "executes from the start of the function **until the first yield statement**". The `push` is after the first yield statement, so it never gets called. The interesting question to me is, why is *anything* pushed. You would think it would just keep yielding the passed in value and never run the `push`. – Heretic Monkey Aug 25 '22 at 18:56
  • Ah make sense, the first one calls till the yield. I quoted but just now understood clearly – Naren Aug 25 '22 at 19:05
3

This is a limitation of generator functions. If you really want to be able to use the first argument, you can construct your own iterator manually.

const gen = () => {
  const argumentsPassedIn = [];
  const makeObj = () => ({
    done: false,
    value: undefined,
    next: (arg) => {
      argumentsPassedIn.push(arg);
      console.log(argumentsPassedIn);
      return makeObj();
    },
  });
  return makeObj();
}


const g = gen();
g.next("A");
g.next("B");
g.next("C");
g.next("D");
g.next("E");
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
1

The parameter passed to the first call to .next() is ignored as of ES2022. This is because the first call to .next() runs the function until the first yield or return is encountered and the following calls will make yield operator return the value passed as a parameter. This is usually solved by calling .next() unconditionally after calling the generator function.

There is, however, a stage 2 proposal that aims to solve this problem by introducing new syntax to get the value passed to the .next() method that most recently resumed execution of the generator.

D. Pardal
  • 6,173
  • 1
  • 17
  • 37