作用迭代器
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 的返回值和执行过程的错误都会传入全局的回调函数