[Javascript] ES6 Generator基礎

Posted by Huli on 2015-08-24

最近趁著有些時間研究了一下ES6,有個很重要的東西叫做Generator,但是這概念對我來說超級陌生
所以我覺得沒有很好上手,研究過一些資料以後終於搞懂是在做什麼
寫篇文章跟大家分享

首先,Generator這東西之所以會不好上手,是因為
##一個指令做兩件事情
只要記住這個原則,Generator就沒有那麼困難了

1
2
3
4
5
6
7
8
9
10
11
12
13
function *get_counter(){
let i = 1;
while(true){
yield i;
i++;
}
}
var counter = get_counter();
console.log(counter.next().value);//1
console.log(counter.next().value);//2
console.log(counter.next().value);//3
console.log(counter.next().value);//4

一個簡單的計數器,在這邊可以很簡單的先想說,yield這個指令就是把東西丟出去
這邊應該滿好理解,但是generator難懂的地方就在於,除了把東西丟出去,還可以丟東西進來

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function *get_adder(){
let total = 0;
while(true){
console.log("before yield");
total+=yield total;
console.log("after yield, total:"+total);
}
}
var adder = get_adder();
console.log(adder.next().value);
/*
before yield
0
*/
console.log(adder.next(100).value);
/*
after yield, total:100
before yield
100
*/

在執行第一次next()的時候,會先跑到console.log("before yield");,這很好懂
接著會先執行yield total,會先把total丟出去,所以會輸出0
接下來呢?還記得剛剛說過 yield除了丟值,還可以接受值,所以丟完以後,現在會等下一個值丟進來
於是程式就停住了,執行完第一次之後,會停留在輸出0的地方

接著執行第二次next(),我們把100傳進去,於是上一次的yield在等待的值就傳入了,會執行
total+=100;,然後輸出after yield, total:100
每呼叫一次next,都會跑到yield輸出值的地方!所以接著會跑到before yield然後把total,也就是100丟出去
接著等待下一個值

用比較好懂的方式,就是把yield拆成兩個指令

1
2
3
4
5
6
7
8
9
function *get_adder(){
let total = 0;
while(true){
console.log("before yield");
output(total);
total+=input();
console.log("after yield, total:"+total);
}
}

每次next()的時候都會執行到output的地方
input則是會等待外面傳值進來

再來看一個範例

1
2
3
4
5
6
7
8
9
10
11
function *gen(){
let arr = [];
while(true){
arr.push(yield arr);
}
}
var name = gen();
console.log(name.next('init').value);//[]
console.log(name.next('nick').value);//["nick"]
console.log(name.next('peter').value);//["nick","peter"]

可以注意到的是,第一次的next()無論有沒有傳值其實都一樣
為什麼呢?還記得剛才提過,generator最重要的概念是:每次next()都會執行到yield丟值出去的地方
然後yield這個指令分成兩個步驟,先丟東西出去,再等東西進來
所以第一次的next(),跑到yield的第一個步驟就結束了,等第二次的next()傳東西進來

差不多就這樣了
重要的兩個概念就是:

  1. yield其實是兩個動作的合體:丟東西出去->等東西進來
  2. 每次next()都會跑到yield丟東西出來的那個步驟

ref:
拥抱Generator,告别异步回调
ES6 Generators 基礎教學