Generator Back
Generator is a concept specified in ECMAScript 2015 (ES6), which stands for state machines like objects, which have an iterable protocol. It means that we can easily use the Generator.prototype.next()
method to access the next state of such a machine.
In JavaScript, if you want to create a generator, you should use function*
rather than Generator
, as it is not a constructor.
function* gen() {
yield 1;
yield 2;
}
/**
* a generator
* @type {IterableIterator<number>}
*/
const generator = gen();
With such an object, there are three exposed methods we can use:
Generator.prototype.next()
: returns a value yielded by theyield
expression.const generator = gen(); generator.next(); // => {value: 1, done: false} generator.next(); // => {value: 2, done: false} generator.next(); // => {value: undefined, done: true}
Generator.prototype.return()
: returns the given value and finishes the generator.const generator = gen(); generator.next(); // => {value: 1, done: false} generator.return(); // => {value: undefined, done: true} generator.next(); // => {value: undefined, done: true}
Generator.prototype.throw()
: throws an error to the generator, and finishes it, unless the error caught by the generator.const generator = gen(); generator.next(); // => {value: 1, done: false} generator.throw(1); // => Uncaught 1 generator.next(); // => {value: undefined, done: true}
So how can we use generators to describe a state machine? Take the following case as an example. Assume that there is a state machine like this:
And then the generator for this can code like this:
function* machine() {
// the start of state
let state = 0;
let action;
// simple prev checker
const _next = (prev, val) => state === prev ? val : state;
for (;;) {
/**
* get the initial state, and read "action"
* passed by `Generator.prototype.next()` each time
*/
action = yield state;
state = _next.apply(0, ({'E': [0, 1], ';': [1, 2], 'L': [2, 3], 'ε': [0, 3]})[action]);
// the end of state
if (state === 3) return state;
}
}
const generator = machine();
generator.next(); // need to call next at first for initializing states
generator.next('E'); // => {value: 1, done: false}
generator.next(';'); // => {value: 2, done: false}
generator.next('L'); // => {value: 3, done: true}
const generator = machine();
generator.next(); // need to call next at first for initializing states
generator.next('ε'); // => {value: 3, done: false}
Quite easy, right? I have wrapped it as a simple library: @aleen42/state. In addition, we will take a look at some usages of yield
:
If we want to combine two generators, or embed one into another, we can use
yield *
:function* top(i) { yield i + 1; yield i + 2; yield i + 3; } function* decade(i) { yield i; yield * top(i); yield i + 10; } const generator = decade(10); generator.next(); // => {value: 10, done: false} generator.next(); // => {value: 11, done: false} generator.next(); // => {value: 12, done: false} generator.next(); // => {value: 13, done: false} generator.next(); // => {value: 20, done: false}
Pass arguments into generators:
function* log() { console.log(0); console.log(1, yield); } const generator = log(); generator.next('first'); // => 0 generator.next('second'); // => 1 second
As we can see in the snippet above, that's why we need to initialize states at first with calling
Generator.prototype.next()
.Return statements in a generator:
function* method() { yield 'a'; return 'close'; yield 'unreachable'; // the generator will be marked as done once it returns } const generator = method(); generator.next(); // => {value: "a", done: false} generator.next(); // => {value: "close", done: true} generator.next(); // => {value: undefined, done: true}
Generator as an object property:
const obj = { * gen() { yield 1; yield 2; }, }; const generator = obj.gen();
Generator as an class member:
class Component { constructor() {} * gen() { yield 1; yield 2; } } const component = new Component(); const generator = component.gen();
As the plugin is integrated with a code management system like GitLab or GitHub, you may have to auth with your account before leaving comments around this article.
Notice: This plugin has used Cookie to store your token with an expiration.