1. 제너레이터(Generator)
- 제너레이터(Generator) 는 함수 실행을 일시 중지하고 다시 시작할 수 있는 특별한 함수이다.
- 일반 함수와 달리, 제너레이터는
function* 키워드로 정의되며, 호출 시 즉시 실행되지 않고 이터레이터(Iterator) 객체를 반환한다.
- 이를 통해 값을 순차적으로 생성하거나 제어할 수 있는데, 제너레이터는 특히 비동기 처리나 큰 데이터 집합을 단계별로 처리할 때 유용하다.
2. iterable, iterator
- 제너레이터는 iterable하고 iterator한 특징을 가지는데 각 내용은 아래와 같다.
iterable
- JavaScript에서
iterable은 반복 가능한 객체를 의미하며, for...of 루프나 전개 연산자(...) 등과 같은 반복문에서 사용할 수 있는 객체이다.
- 이 반복 가능한 객체(
iterable한 객체)는 항상 Symbol.iterator() 메소드를 구현하고 있어야 하며, 이 메소드는 객체가 어떻게 반복될지 정의한다.
iterator
- 반복 작업을 제어하는 객체로, 반복되는 시퀀스의 값을 하나씩 반환할 수 있다.
next() 메서드를 호출할 때마다 순차적으로 값을 반환하며, 반복이 끝나면 done: true가 반환된다.
3. 제너레이터의 특징
- 제너레이터 함수를 실행하면, iterator한 제너레이터 객체가 반환된다.
- 반환된
iterator 객체가 갖고있는 next() 메소드를 사용하면, 가장 가까운 yield 키워드를 만날 때까지 코드를 실행할 수 있다.
3-1. 제너레이터 함수 정의
function* myGenerator() {
yield 1;
yield 2;
yield 3;
}
function* 키워드와 함께 yield 키워드를 사용해 값을 생성한다.
3-2. 제너레이터 호출
function* fn() {
console.log(1);
yield 1;
console.log(2);
yield 2;
console.log(3);
console.log(4);
yield 3;
console.log(5);
return "finish!!";
}
const a = fn();
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
- 제너레이터 함수 호출은 iterator 객체를 반환하며,
next() 메서드를 호출해 하나씩 값을 얻을 수 있다.
3-3. return 메소드를 사용한 제너레이터 중지
function* func() {
console.log(11);
yield 1;
console.log(22);
yield 2;
console.log(33);
console.log(44);
yield 3;
console.log(55);
return "finish!!";
}
const a = func();
console.log(a.next());
console.log(a.next());
console.log(a.return());
console.log(a.next());
- 제너레이터 객체의 return 메소드
func().return()를 사용하면, 즉시 done은 true가 되고, value는 undefined가 된다.
iterator.next().return()을 사용하면, 이후 yield 키워드를 만날 때까지 함수 내부의 코드를 실행하는 것이 아니라, 바로 그 즉시 함수 실행을 종료하기 때문에 value가 undefined가 된다.
4. 제너레이터 활용 예시
- 무한 시퀀스 생성
function* infiniteSequence() {
let i = 0;
while (true) {
yield i++;
}
}
const seq = infiniteSequence();
console.log(seq.next().value);
console.log(seq.next().value);
- iterable 객체 생성
function* range(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
for (let value of range(1, 5)) {
console.log(value);
}
- 제너레이터는 기본적으로
iterable 객체이므로 for...of 루프에서 사용할 수 있다.
- 이외에도 제너레이터는
yield를 사용해 중간 중간 작업을 멈추고 다시 시작할 수 있어, 비동기 흐름을 관리하기에도 좋다.