在新的 ES 標(biāo)準(zhǔn)中侣姆,生成器(Generator) 幾乎是一個(gè)完全嶄新的函數(shù)類型夜郁,它能生成一組值得序列,但每個(gè)值的生成是基于每次請求卿闹,并不同于標(biāo)準(zhǔn)函數(shù)那樣立即生成揭糕。
這樣特殊的性質(zhì)萝快,也使得它成為了我們解決 JS 異步編程的一個(gè)利器。
生成器的遍歷
要遍歷生成器很簡單著角,只需要用for...of
函數(shù)就可以取出每一步從生成器中返回的值揪漩。舉個(gè)例子:
function* AnimalGenerator() {
yield 'dog';
yield 'cat';
yield 'fish';
}
for(let animal of AnimalGenerator()) {
console.log(animal);
}
// => dog cat fish
生成器嵌套
生成器嵌套的方式,可以將一個(gè)生成器委托給另外一個(gè)生成器吏口,在執(zhí)行到某個(gè)節(jié)點(diǎn)的時(shí)候奄容,返回該生成器中的值:
function* AllGenerator() {
yield 'human';
yield* AnimalGenerator();
yield 'AI';
}
for(let item of AllGenerator()) {
console.log(item);
}
// => human dog cat fish AI
迭代器
在調(diào)用生成器之后,會(huì)創(chuàng)建一個(gè) 迭代器(Iterator) 并返回出來产徊,而利用迭代器就可以逐步返回生成器中的值:
const animalIterator = AnimalGenerator();
const animal1 = animalIterator.next();
const animal2 = animalIterator.next();
const animal3 = animalIterator.next();
const animal4 = animalIterator.next();
console.log(animal1, animal2, animal3, animal4);
// => {value: "dog", done: false} {value: "cat", done: false} {value: "fish", done: false} {value: undefined, done: true}
在每次執(zhí)行迭代器next
方法的時(shí)候昂勒,返回一個(gè)對(duì)象,對(duì)象包含兩個(gè)屬性:
-
value:生成器的返回值舟铜,生成結(jié)束之后返回
undefined
; -
done:表示生成器是否生成完畢戈盈,返回
false
或true
。
除此之外谆刨,迭代器的next
還可以接收一個(gè)參數(shù)作為上一次yield
的返回值:
function* Next(x) {
const y = 3 * yield (x + 1);
const z = yield (y / 10);
yield (x + y + z);
}
const next = Next(1);
const result1 = next();
// => value: 2
const result2 = next(10);
// => value: 3
const result3 = next(12);
// => value: 43
這結(jié)果看起來有點(diǎn)繞塘娶,但是捋一捋就會(huì)發(fā)現(xiàn)很有意思:
- 第一次調(diào)用
next
,返回值是第一個(gè)yield
痊夭,因此就是x+1
刁岸,即2
; - 第二次調(diào)用
next(10)
她我,傳入的值替代了第一個(gè)yield
的返回值虹曙,所以y
的值為3*10
,即30
鸦难,返回第二個(gè)yield
就是30/10
根吁,即3
; - 第三次調(diào)用
next(12)
合蔽,傳入的值替代了第二個(gè)yield
,所以z
的返回值為12
介返,加上之前生成器緩存的x=1
和y=30
拴事,最終返回x+y+z
的值就是43
。
生命周期
看了這些生成器的基本用法圣蝎,肯定會(huì)對(duì)它的內(nèi)部原理很好奇刃宵。概括一下,就是直接調(diào)用一個(gè)生成器不會(huì)直接執(zhí)行徘公,而是會(huì)創(chuàng)建一個(gè)新的迭代器返回出來牲证,通過迭代器我們才能對(duì)其進(jìn)行求值。生成器每生成一個(gè)值都會(huì)自己掛起執(zhí)行关面,然后等待下一個(gè)請求坦袍。
在某種當(dāng)面來說十厢,生成器的工作更像是一個(gè)在狀態(tài)中運(yùn)動(dòng)的狀態(tài)機(jī)。
如上圖所示捂齐,生成器的生命周期主要分為四個(gè)階段:
- Start:創(chuàng)建一個(gè)生成器以后蛮放,不執(zhí)行任何代碼,然后掛起奠宜;
-
Exucuting:執(zhí)行生成器的代碼包颁,通過迭代器的
next
方法調(diào)用,只要還有可執(zhí)行代碼压真,生成器都會(huì)進(jìn)入這個(gè)狀態(tài)娩嚼; -
Yield:生成器每執(zhí)行一次,都到
yield
表達(dá)式處包裹一個(gè)對(duì)象返回滴肿,然后掛起等待執(zhí)行待锈; -
Completed:如果代碼全部執(zhí)行完畢,或者遇到
return
嘴高,就執(zhí)行完畢竿音。
從生命周期我們可以看到,在整個(gè)周期里拴驮,只要我們?nèi)〉蒙善鞯目刂茩?quán)春瞬,它的執(zhí)行上下文就會(huì)一直保存,而不會(huì)想標(biāo)準(zhǔn)函數(shù)一樣退出后就銷毀套啤。