본문 바로가기

JavaScript

[JavaScript] 제너레이터와 이터레이터

1. 제너레이터 함수? 

ES6에 도입된 제너레이터(Generator) 함수는 이터러블을 생성하는 함수이다. 

이전에 사용한 이터레이션 프로토콜을 준수해서 사용자 생성 방식보다 간단하게 이터러블을 구현할 수 있다. 

 

또, 제너레이터 함수는 비동기 처리에 유용하게 사용된다. 

 

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

const genlter = gen();

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

제너레이터 함수는 일반적인 이터러블과 동일하게 동작하는 것처럼 보인다. 

 

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

const genlter = gen();

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

하지만 제너레이터 함수는 일반 함수와 같이 함수의 코드 블록을 한 번에 실행하지 않고 함수 코드 블록의 실행을

일시 중지했다가 필요한 시점에 재시작할 수 있는 특수한 함수이다. 

 

즉, 제너레이터는 문장을 통해 순회할 수 있는 값을 만들 수 있으며, 어떤 값도 제너레이터를 통해 순회할 수 있는 

형태로 조작할 수 있다. 

 

2. 제너레이터 함수 정의 

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

const genlter = gen();

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

제너레이터 함수의 선언은 funtion 앞에 *을 붙여서 function* 함수명 () {} 형식으로 선언하면 된다. 

그리고 yield를 통해서 몇 번의 next()를 할지 결정할 수 있다. 

 

return값을 설정하면 done이 true가 된 시점에 value 값을 리턴할 수 있다. 

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

 

3. 제너레이터 함수 활용 

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

function* odds(num) {
	for(let i = 0; i < num; 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(num, iter){
    for(const a of iter){
    	yield a;                                 // --- 1
        if(a === num) return;
    }
}

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

1. 신기한? 부분이 limit을 호출하고 yield a; 라인까지 딱 실행된 다음에 다시 호출되면

   아랫줄인 if(a === num) return 이 호출된 다음에 다시 for문이 실행된다. 

 

 

반응형