본문 바로가기

Web[웹]/ES6+ 함수형 언어

[JS: ES6+] 제너레이터와 이터레이터

#1 제너레이터와 이터레이터

 

제너레이터 함수란?

 

ES6에서 도입된 제너레이터(Generator) 함수는 이터러블을 생성하는 함수이다. 제너레이터 함수를 사용하면 이터레이션 프로토콜을 준수해 이터러블을 생성하는 방식보다 간편하게 이터러블을 구현할 수 있다. 또한 제너레이터 함수는 비동기 처리에 유용하게 사용된다.

 

function* gen() {
   yield 1;
   yield 2;
   yield 3;
}

const genIter = gen();

console.log(genIter.next()); // {value:1, done: flase}
console.log(genIter.next()); // {value:2, done: flase}
console.log(genIter.next()); // {value:3, done: flase}
console.log(genIter.next()); // {value:undefined, done: true}

 

위 코드를 보면 제너레이터 함수는 일반 함수와 동일한 동작을 하는것 처럼 보인다. 하지만 아래 코드를 보자.

 

function* gen() {
   console.log('첫번째');
   yield 1;
   console.log('두번째');
   yield 2;
   console.log('세번째');
}

const genIter = gen();

console.log(genIter.next()); // 첫번째 {value:1, done: flase}
console.log(genIter.next()); // 두번째 {value:2, done: flase}
console.log(genIter.next()); // 세번째 {value:undefined, done: true}

 

다시말해서, 제너레이터 함수는 일반 함수와는 다른 동작을 하는데, 제너레이터 함수는 일반 함수와 같이 함수의 코드 블록을 한 번에 실행하지 않고 함수 코드 블록의 실행을 일시 중지했다가 필요한 시점에 재시작할 수 있는 특수한 함수이다. 즉, 제너레이터는 문장을 통해 순회할 수 있는 값을 만들 수 있으며, 어떠한 값도 제너레이터를 통해 순회할 수 있는 형태로 조작할 수 있다.

 


제너레이터 함수의 정의

 

먼저 제너레이터 함수를 선언하고 이용하는 방법을 간단하게 알아보자.

 

function* gen() {
   yield 1;
   yield 2;
   return 100;
}

const genIter = gen();

console.log(genIter.next()); // {value:1, done: flase}
console.log(genIter.next()); // {value:2, done: flase}
console.log(genIter.next()); // {value:100, done: true}

 

제네레이터 함수의 선언은 function 앞에 *을 붙여서 function* func() 형식으로 선언하면 된다. 그리고 yield를 통해서 몇 번의 next()를 할 수 있는지를 결정할 수 있으며, return값을 설정함으로써 done이 true가 되는 시점에 value값을 리턴할 수 있다.

※단, 기본적으로 순회하면서 조회할때는 리턴값은 무시된다.

 


제네레이터 함수의 활용

 

제네레이터 함수를 활용해 간단하게 홀수만 계속해서 발생시키는 이터레이터를 만들어보자.

 

function* odds(l){
   for (let i = 0; i < l; i++){
      if (i % 2) yield i;
   }
}

const genIter = odds(10); 

console.log(genIter.next()); // {values: 1, done: false}
console.log(genIter.next()); // {values: 3, done: false}
console.log(genIter.next()); // {values: 5, done: false}
console.log(genIter.next()); // {values: 7, done: false}
console.log(genIter.next()); // {values: 9, done: false}
console.log(genIter.next()); // {values: undefined, done: true}

 

다음과 같이 활용도 가능하다.

 

function* infinity(i = 0) {
   while (true) yield i++;
}

function* limit(l, iter) {
   for (const a of iter) {
      yield a;
      if (a == l) return;
   }
}

function* odds(l){
   for (const a of limit(l, infinity(1))) {
      if (a % 2) yield a;
   }
}

const genIter = odds(10); 

console.log(genIter.next()); // {values: 1, done: false}
console.log(genIter.next()); // {values: 3, done: false}
console.log(genIter.next()); // {values: 5, done: false}
console.log(genIter.next()); // {values: 7, done: false}
console.log(genIter.next()); // {values: 9, done: false}
console.log(genIter.next()); // {values: undefined, done: true}