javascriptに限らずIteratorの仲間としてGeneratorをサポートした言語は他にもあります。なぜ同じ名前で呼ばれるかと言うともともと共通の概念を指すからだと思います。
Generatorは通常Iteratorの実装方法の一つとして捉えることができ、返すべき値をコルーチンとして書けるというのが本質的な特徴だと思います。
例としてPythonのrange(from, to)をJavascriptで書いてみますと・・・
Javascript
1
2// 普通の関数としてIteratorを実装した例
3
4function range(from, to) {
5 let r = {}
6 r[Symbol.iterator] = () => {
7 let i = from
8 return {
9 next() {
10 return i < to
11 ? { done: false, value: i++ }
12 : { done: true }
13 }
14 }
15 }
16 return r
17}
18
19// GeneratorとしてIteratorを実装した例
20
21function g_range(from, to) {
22 let r = {}
23 r[Symbol.iterator] = function* () {
24 for (let i = from; i < to; i++) {
25 yield i
26 }
27 }
28 return r
29}
30
31for (let i of range(1, 4)) {
32 console.log(i)
33}
34
35for (let i of g_range(1, 4)) {
36 console.log(i)
37}
1
2
3
1
2
3
上記を踏まえて元の質問に戻りますと・・・
1つ目は関数と違って途中で処理を中断出来、また再開できるということ
これをコルーチンといいます
2つ目は使う側が「くれ!」というと、その度にiteratorと同じように提供できる値が尽きるまで「どうぞ!」と渡してくれるということ、
これはgeneratorの特徴というよりIteratorの特徴です。
3つ目は、iteratorにはない特徴として、next()の引数に値をgenerator側に渡すことで、generatorとやりとりができるということかなと思います。
それは少々違うように思います。IteratorもGeneratorも「どのような列挙をしてほしいかを引数で指定するように実装できます」上記のrange, g_rangeが単純な例になっているかと思います。
何に使えるのかな
Iteratorを実装したい場面で複雑な場合分けなどを逐次処理的に書きたい場合はgeneratorとして記述した方が分かり易くなると言えます。それはコルーチンであるがゆえの特徴と言えるのではないでしょうか。
例えば1~3まで1おきに、次に10~30まで10おきの値を返すようなIteratorを定義してみますと
Javascript
1function it() {
2 let i = 1
3 return {
4 next() {
5 if (i < 4)
6 return { done: false, value: i++ }
7 if (i == 4)
8 i = 10
9 return i < 40
10 ? { done: false, value: i += 10 }
11 : { done: true }
12 }
13 }
14}
なんかnextの実装でやっている条件分岐がややこしいですよね。これをGeneratorでかくと非常に平易に書けます。
Javascript
1function* git() {
2 for (let i = 1; i < 4; i++)
3 yield i
4 for (let i = 10; i < 40; i += 10)
5 yield i
6}
単純な列挙をするなら普通の関数としてIteratorを実装してもGeneratorを使っても大差ない気がしますが、ちょっと複雑な列挙をしようとするときGeneratorの記述力の有用性が実感できるのではないでしょうか?