By using yield
, generators can be suspended at any point in the control flow of your function, saving your current state of execution (scope & stack).
Without generators, this is more complicated:
- you need to explicitly keep track of the state
- branching and (especially) looping control structures need to be represented in a functional way, i.e. written recursively.
Generators are generically useful for traversing data structures, creating a simple stream-like iterator that yields all elements in order. Think of tree traversal, or DFS/BFS in graphs for simple examples.
function* traverseTree(node) {
if (node == null) return;
yield* traverseTree(node.left);
yield node.value;
yield* traverseTree(node.right);
}
// vs (not sure):
function traverseTree(node) {
var rl, l, r;
return {
next: function() {
if (node == null && !r) return {done:true};
if (!l) l = traverseTree(node.left);
if (!(rl=l.next()).done)
return rl;
if (node != null) {
var n = {value:node.value};
node = null;
r = traverseTree(node.right);
return n;
}
return r.next();
}
}
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…