作用迭代器

function* argumentsGenerator() {
  for (let i = 0; i < arguments.length; i++) {
    yield arguments[i];
  }
}
`</pre>

我们希望迭代传入的每个实参

<pre>`var argumentsIterator = argumentsGenerator('a','b','c','d');

// Prints "a,b,c,d"
console.log(
  argumentsIterator.next().value,
  argumentsIterator.next().value,
  argumentsIterator.next().value,
  argumentsIterator.next().value
)
`</pre>

我们可以简单的理解
- Generator 其实是生成 Iterator 的方法, argumentsGenerator 被称为 GeneratorFunction, 也有一些人把 GeneratorFunction 的返回值称为一个 Generator
- yield 可以中断 GeneratorFunction 的运行, 而在 next() 时恢复运行
- 返回的 Iterator 上, 有 next 成员方法, 能够返回迭代值. 其中 value 属性包含了实际返回值, done 属性为布尔值, 标记迭代器是否完成迭代, 要注意的是, 在 done: true 后继续运行 next 方法会产生异常.

完整的 ES 实现中, for-of 循环正式为了快速迭代一个 Iterator

<pre>`for (let value of argumetnsIterator) {
  console.log(value);
}
`</pre>

可以利用 yield* 语法, 将 yield 操作代理到另一个 Generator

<pre>`let delegatedIterator = (function*() {
  yield 'Hello!';
  yield 'Bye!';
})();

let delegatingIterator = (function*() {
  yield 'Greetings!';
  yield* delegatedIterator;
  yield 'Ok, bye!';
})();

// Prints "Greetings!", "Hello", "Bye!", "Ok, bye!"
for (let value of delegatingIterator) {
  console.log(value);
}

用作流程控制

co 已经将此特性封装的非常完美

Generator 之所以可以用来控制代码流程, 就是通过 yield 来将两个或多个 Generator 的执行路径相互切换, 这种切换是语句级别的, 而不是函数级调用. 其本质是 CPS 变换.

这里补充 yield 的若干行为:

  • next 方法接收一个参数, 传入的参数是 yield 表达式的返回值: 即 yield 既可以产生数值, 也可以接收数值
  • throw 方法会抛出一个异常, 并终止迭代
  • GeneratorFunction 的 return 语句相当于一个 yield

将异步”变为”同步

假设我们希望有如下语法:

  • suspend 传入一个 GeneratorFunction
  • suspend 返回一个简单函数, 接收一个 node 风格的回调函数
  • 所有的异步调用都通过 yield, 看起来像同步调用
  • 给定一个特殊的回调, 让保证异步调用的返回值作为 yield 的返回值, 并让脚本继续
  • GeneratorFunction 的返回值和执行过程的错误都会传入全局的回调函数