Search code examples
javascriptgeneratoryield

Can I get the current value of generator in JavaScript?


Let's say I want to rotate class names for my button on click. Clicked once becomes button-green, twice - button-yellow, thrice - button-red. And then it repeats, so fourth click makes it button-green again.

I know other techniques how to do it, I'm not asking for implementation advice. I made up this example to understand something about generators in JavaScript.

Here's my code with generator:

function* rotator(items) {
    while (true) {
        for (const item of items) {
            yield item;
        }
    }
}

const classRotator = rotator([
    'button-green',
    'button-yellow',
    'button-red',
]);

document.getElementById('my-button').addEventListener('click', event => {
    event.currentTarget.classList.add(classRotator.next().value);
});

It works fine, except it never gets rid of the previous class. The most convenient way would be to read the current state before getting the next one:

// .current is not a thing:
event.currentTarget.classList.remove(classRotator.current);

Of course I can keep this value on my own and use it. Likewise, I can clear all classes I use in the rotator() myself. I can even make my generator function yield both previous and current value:

function* rotator(items) {
    let previous;

    while (true) {
        for (const item of items) {
            yield {
                item,
                previous
            };

            previous = item;
        }
    }
}

And then use it like this:

document.getElementById('my-button').addEventListener('click', event => {
    const {item: className, previous: previousClassName} = classRotator.next().value;

    event.currentTarget.classList.remove(previousClassName);
    event.currentTarget.classList.add(className);
});

But that's not the point - for educational purpose I'm asking this question:

Can I read the current value of generator function in JavaScript? If not, is it to avoid using memory when it's not needed (this value can potentially be very big)?


Solution

  • JavaScript "native" APIs generally are willing to create new objects with wild abandon. Conserving memory is generally not, by any appearances, a fundamental goal of the language committee.

    It would be quite simple to create a general facility to wrap the result of invoking a generator in an object that delegates the .next() method to the actual result object, but also saves each returned value as a .current() value (or whatever works for your application). Having a .current() is useful, for such purposes as a lexical analyzer for a programming language. The basic generator API, however, does not make provisions for that.