Generator
在异步编程中,还有一种常用的解决方案,它就是Generator生成器函数。顾名思义,它是一个生成器,它也是一个状态机,内部拥有值及相关的状态,生成器返回一个迭代器Iterator对象,我们可以通过这个迭代器,手动地遍历相关的值、状态,保证正确的执行顺序。
声明
Generator 的声明方式类似一般的函数声明,只是多个*号,并且一般可以再函数内看到yield关键字
function* showWords() {
yield 'one';
yield 'two';
return 'three';
}
var show = showWords();
show.next() // {done: false, value: "one"}
show.next() // {done: false, value: "two"}
show.next() // {done: true, value: "three"}
show.next() // {done: true, value: undefined}
调用new方法后,函数执行第一条yield语句,输出当前的状态done(迭代器是否遍历完成)以及相应值(一般为yield关键字后面的运算结果)
每调用一次next,则执行一次yield语句,并且在该处暂停。return完成之后,就退出了生成器函数,后续如果还有yield操作也不再执行。
yield 和 yield*
类似于生成器前面的*号,yield后面的星号也跟生成器有关
function* showWords() {
yield 'one';
yield showNumbers();
return 'three';
}
function* showNumbers() {
yield 10 + 1;
yield 12;
}
var show = showWords();
show.next() // {done: false, value: "one"}
show.next() // {done: false, value: showNumbers}
show.next() // {done: true, value: "three"}
show.next() // {done: true, value: undefined}
增添了一个生成器函数,我们想在showWords中调用一次,简单的 yield showNumbers()之后发现并没有执行函数里面的yield 10+1
因为yield只能原封不动地返回右边运算后值,但现在的showNumbers()不是一般的函数调用,返回的是迭代器对象
所以换个yield* 让它自动遍历进该对象
function* showWords() {
yield 'one';
yield* showNumbers();
return 'three';
}
function* showNumbers() {
yield 10 + 1;
yield 12;
}
var show = showWords();
show.next() // {done: false, value: "one"}
show.next() // {done: false, value: 11}
show.next() // {done: false, value: 12}
show.next() // {done: true, value: "three"}
注意:
yield和yield*只能在generator函数内部使用,否则会报错
function showWords() {
yield 'one'; // Uncaught SyntaxError: Unexpected string
}
对没有Iterator接口,没有提供yield遍历的也会报错
function showWords() {
yield* 'one';
}
var show = showWords();
show.next() // Uncaught ReferenceError: yield is not defined
next()调用中的传参
function* showNumbers() {
var one = yield 1;
var two = yield 2 * one;
yield 3 * two;
}
var show = showNumbers();
show.next().value // 1
show.next().value // NaN
show.next(2).value // 6
第一次调用next之后返回值one为1,但在第二次调用next的时候one其实是undefined的,因为generator不会自动保存相应变量值,我们需要手动的指定,这时two值为NaN,在第三次调用next的时候执行到yield 3 * two,通过传参将上次yield返回值two设为2,得到结果
即:传入的值就作为上一个返回的值
for...of循环代替.next()
除了使用.next()方法遍历迭代器对象外,通过ES6提供的新循环方式for...of也可遍历,但与next不同的是,它会忽略return返回的值,如
function* showNumbers() {
yield 1;
yield 2;
return 3;
}
var show = showNumbers();
for (var n of show) {
console.log(n) // 1 2
}