JavaScript
Generator
- 함수에 실행을 중간에 멈췄다가 재개할 수 있는 독특한 기능을 한다.
function
옆에*
을 써서 만들고 함수 내부에yield
키워드를 사용한다.yield
키워드로 함수를 멈출 수 있다.next
키워드로 사용되고,next
를 사용하기 전까지 함수 내부의 코드는 실행되지 않는다.next
를 사용 시, 가장 가까운yield
문을 만날 때까지 실행되고 데이터 객체를 반환한다.- 반환 된 객체는
value
와done
프로퍼티를 가지는데,value
는yield
에 오른쪽에 작성된 값이다. done
은 함수가 끝났는 지를 나타내며, 함수가 끝났으면true
, 끝나지 않았으면false
로 나타낸다.next
를 사용하여 다른 작업을 하다가 다시 돌아와서, 멈춰있던 부분부터 이어서 실행하게 된다는 장점이 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function* f1() {
yield 1;
yield 2;
yield 3;
return "finish";
}
const a = fn();
a; // f1{<suspended>}
a.next(); // {value: 1, done: false}
a.next(); // {value: 2, done: false}
a.next(); // {value: 3, done: false}
a.next(); // {value: "finish", done: true}
a.next(); // {value: undefined, done: true}
return()
return()
을 사용하여 즉시 함수를 종료할 수 있다.
1
2
3
4
5
6
7
8
9
10
function* f1() {
yield 1;
yield 2;
yield 3;
return "finish";
}
const a = fn();
a.return("End"); // {value: "End", done: true}
throw()
throw()
를 통해try...catch
문을 실행할 수 있다.- 마찬가지로 즉시 함수를 종료한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function* f1() {
try {
yield 1;
yield 2;
yield 3;
return "finish";
} catch (e) {
return e;
}
}
const a = fn();
a.throw(new Error("err")); // {value: "Error: err...", done: true}
iterable, iterator
iterator
란 반복 가능한 객체를 순회하는 데 사용되는 객체이다.- 배열의
prototype
에는Symbol.iterator
메서드가 있는데iterator
의next
메서드는value
와done
속성을 가진 객체를 반환한다. - 배열에서
for...of
와 마찬가지로Generator
또한 순회하며 반복하기 때문에, 구조분해 할당 시done
이true
가 될 때까지 값을 펼쳐주는 역할을 한다.
1
2
3
4
5
// 반복 가능한 객체이다.
// 문자열도 iterable이다.
const arr = [1, 2, 3, 4, 5];
arr[Symbol.iterator](); // Array Iterator {}
외부 인자
next
안에 입력한 값은yield
에 할당된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function* f1() {
const num1 = yield "첫 번째 숫자";
console.log(num1);
const num2 = yield "두 번째 숫자";
console.log(num2);
return num1 + num2;
}
const a = f1();
a.next(1);
// {value: "첫 번째 숫자", done: false}
// 1 (next(1)이 할당됨)
a.next(15);
// {value: "두 번째 숫자", done: false}
// 15
a.next(); // {value: 16, done: true}
Generator
는 외부로부터 값을 전달받을 수 있기 때문에, 값을 미리 만들어두지 않는다.- 필요한 순간에만 사용되기 때문에 메모리 측면에서 효율적이다.
- 예시로,
break
가 없는while
문을 사용하더라도 필요한 순간에만 사용되기 때문에 사실상 무한 반복되지 않는다.
1
2
3
4
5
6
7
8
9
10
11
12
function* f1() {
let index = 0;
while (true) {
yield index++;
}
}
const a = f1();
a.next(); // {value: 1, done: false}
a.next(); // {value: 2, done: false}
a.next(); // {value: 3, done: false}
a.next(); // {value: 4, done: false}
yield*
yield
을 사용하여 다른Generator
를 참조할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
function* f1() {
yield "H";
yield "i";
}
function* f2() {
yield "Say";
yield* f1();
yield* "!";
}
console.log(...f2()); // Say H i !