Search code examples
javascriptc#iteratorgenerator

How to conditionally stop a JavaScript generator?


With an iterator like the following one:

function* idMaker(){
    let index = 0;
    while(index < 3)
        yield index++;
}

How can I make sure to stop the generator prematurely based on a condition?

function* idMaker(){
    let index = 0;
    while(index < 3)
        if (checker(index))
            yield index++;
        else
            return;
}

Is it okay appropriate to use in a generator? Should I use break instead of yield break perhaps like in C#?

Can you include a link to MDN or spec where this is discussed?


Solution

  • I've decided to verify this experimentally with the following code:

    function checker(i) {
      return i !== 2;
    }
    
    function* idMakerReturn(){
        let index = 0;
        while(index < 3)
            if (checker(index))
                yield index++;
            else
                return;
    }
    
    function* idMakerBreak(){
        let index = 0;
        while(index < 3)
            if (checker(index))
                yield index++;
            else
                break;
    }
    
    const a = idMakerReturn();
    console.log('1', a.next());
    console.log('2', a.next());
    console.log('3', a.next());
    
    const b = idMakerBreak();
    console.log('1', b.next());
    console.log('2', b.next());
    console.log('3', b.next());
    

    Both variants correctly return { "done": true, "value": undefined } when broken out from. I did also check this with TypeScript by observing the inferred function type.

    function* idGen(seed: string | undefined) {
      if (!seed) {
        return; // Or `break`
      }
    
      const something = seed.trim();
    }
    

    With return everything works correctly and the function return type is IterableIterator<T>.

    With break I am asked to provide a label to jump to and also something is no longer inferred to be safe to access (just string because the undefined is handled by the if), but remains a string | undefined.

    This leads me to conclude that as Felix says, anything that terminates the generator function will correctly yield the done: true item, but it seems that return is preferable because escape analysis works with it whereas it doesn't with break.

    Still have not been able to find official word on this, but it's good enough for me.