生成器
先看下面的例子
function* quips(name) {
yield "hello " + name + "!";
yield "i hope you are enjoying the blog posts";
if (name.startsWith("X")) {
yield "it's cool how your name starts with X, " + name;
}
yield "see you later!";
}
上面的函数就是生成器函数,和普通函数的不同在于:
- 使用
function*
定义生成器函数,而普通函数使用function
- 生成器函数里面有
yield
关键字
yield关键字是用来做什么的?
运行下下面的代码:
var iter = quips("Xjorendorff");
console.log(iter.constructor);
//第1个yield
var ret = iter.next();
console.log(ret);
//第2个yield
ret = iter.next();
console.log(ret);
console.log("continue ...")
//第3个yield
ret = iter.next();
console.log(ret);
//第4个yield
ret = iter.next();
console.log(ret);
结果:
GeneratorFunction {}
{ value: 'hello Xjorendorff!', done: false }
{ value: 'i hope you are enjoying the blog posts', done: false }
continue ...
{ value: 'it\'s cool how your name starts with X, Xjorendorff',
done: false }
{ value: 'see you later!', done: false }
可以得出的结论是:
- 在生成器函数中,
yield
关键字可以有多个 -
quips("Xjorendorff")
只是返回一个暂停的生成器对象 - 如果不调用生成器函数对象的
next()
方法,生成器的内部代码不会被执行 - 每调用一次
next()
方法,只执行完下一个yield
关键字修饰的语句,调用完成就挂起,继续执行生成器函数外的其他的语句。 - 多执行一次
next()
语句才能执行是否执行完毕。如果不多执行一次,后面的普通代码无法执行。
可以非常确定的说,生成器里的代码都是单线程的,指示生成器内部暂停,生成器对象维护自己的栈帧状态。
大家可能会有疑问,生成器的作用是什么?
生成器是一种迭代器
ES6中的迭代器不仅仅就是一个内置类,它还是是语言的一个扩展点,如果你想实现自己的迭代器,只需要实现[Symbol.iterator]()
和 next()
函数即可。
先看一个例子,自己实现迭代器:
class RangeIterator {
constructor(start, stop) {
this.value = start;
this.stop = stop;
}
[Symbol.iterator]() { return this; }
next() {
var value = this.value;
if (value < this.stop) {
this.value++;
return {done: false, value: value};
} else {
return {done: true, value: undefined};
}
}
}
// Return a new iterator that counts up from 'start' to 'stop'.
function range(start, stop) {
return new RangeIterator(start, stop);
}
``
使用迭代器:
```javascritp
// This should "ding" three times
for (var value of range(0, 3)) {
console.log("Ding! at floor #" + value);
}
结果:
Ding! at floor #0
Ding! at floor #1
Ding! at floor #2
同样的功能,看生成器如何实现的:
function* range(start, stop) {
for (var i = start; i < stop; i++)
yield i;
}
调用:
// This should "ding" three times
for (var value of range(0, 3)) {
console.log("Ding! at floor #" + value);
}
结果:
Ding! at floor #0
Ding! at floor #1
Ding! at floor #2
结果完全一样。
生成器为什么有这样的能力?
因为生成器就是一种迭代器,所有的生成器都内部实现了next()
和[Symbol.iterator]()
函数。所以可以使用for-of迭代。通过yield
返回那次迭代的值即可。
通过上面的例子,我们可以得出下面的结论:
- 可以用生成器更容易的写一个迭代器
- 可以写更简单的循环
- 可以写一些迭代相关的工具:比如过滤器,对迭代的特殊处理,过滤器的例子如下
function* filter(test, iterable) {
for (var item of iterable) {
if (test(item))
yield item;
}
}
生成器和异步
js的异步调用有多痛苦不用多说了。
有很多解决异步问题的方案,比如Deffed/Promise,Promise nodejs已原生支持。
那么生成器是如何处理异步的呢?
TODO