[백업][가리사니] 자바스크립트 es 6 function* (generator function)
javascript

이 문서는 가리사니 개발자 포럼에 올렸던 글의 백업 파일입니다. 오래된 문서가 많아 현재 상황과 맞지 않을 수 있습니다.

이 문서는 ECMAScript 6 추가 기능을 서술하고 있습니다.

ECMAScript (JavaScript) 6 에 추가된 기능 function* (generator function) 에 대해서 알아보자. 참고 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/function*

generator function 이란?

자바스크립트를 해보왔다면 function 조차도 자료형의 일부처럼 쓸 수 있다는 것을 알고있을것이다. 자바스크립트에서 function은 일급객체에 해당하기 때문이다. 일급객체에 속한 함수를 일급 함수라고 부르며 아래 참고사항을 살펴보자.

javascript 의 일반 함수

그럼 예를들어서 일반적인 함수를 하나 만들어보자.

// 선언 방법만 다를 뿐 동일한 함수이다.
function func1()
{
	console.log('함수1');
}
var func2 = function()
{
	console.log('함수2');
};

func1();
func2();
console.log('일급 객체이니 변수에 넣어보자');
var varFn1 = func1;
var varFn2 = func2;
console.log('실행');
varFn1();
varFn2();

결과 함수1 함수2 일급 객체이니 변수에 넣어보자 실행 함수1 함수2

generator function

일반적으로 오류라고 느껴지는 코드를 사용해보자.

// 무한 generator function
function* infinite_gfn()
{
	var seq = 0;
	while (true)
	{
		yield seq++;
	}
}

var gen = infinite_gfn();

for (var i = 0 ; i < 5 ; i++)
{
	console.log(gen.next().value);
}

해설 일반적인 함수만 사용해 왔다면 infinite_gfn 의 "while (true)" 에서 한번 놀라고 var gen = infinite_gfn(); 에서 두번 놀랄것이다. generator function (이하 gfn : 귀찮으니 줄여부르자..) 에서는 일반 함수의 실행에 해당하는 gfn(); 때 실행되지 않는다. var gen = infinite_gfn(); 즉 이상태에선 gen으로 iterator object 가 반환된다. 즉, 순차적으로 설명하자면.

  1. infinite_gfn 를 선언했다.
  2. infinite_gfn 를 실행하여 infinite_gfn 에 대한 iterator 를 gen에 반환했다.
  3. for 실행 gen.next() 불림
  4. gen.next() 는 infinite_gfn 을 yield 지시자 위치까지 실행한다.
  5. 즉 yield seq++; 상태에서 yield 의 값 즉 0이 value에 들어가게되며, 함수는 그상태로 멈춘다.
  • gen.next()[즉 현재 위치].value
  1. gen.next().value 로 yield 의 값 0을 불러 console.log 를 찍는다.
  2. for 실행 i = 1 위치
  3. 다시 gen.next() 가되면 아까 멈췄던 위치 즉 yield seq++ 에서 다시 함수가 움직이기 시작!
  • while (true) 를 통해 yield seq++ 에서 다시 멈추게된다. 이번에는 값이 1인 상태다.
  1. gen.next().value 로 yield 의 값 1을 불러 console.log 를 찍는다.
  • 이런식으로 5회의 for문 반복 후 종료한다.

그럼 예상되는 답안을 맞춰보자.

// 무한 generator function
function* infinite_gfn()
{
	var msg = '';
	while (true)
	{
		yield msg += '안녕';
		yield msg += '!!';
	}
}

var gen = infinite_gfn();

gen.next();
gen.next();
gen.next();
var val = gen.next().value;
// val의 값음 은 무었일까?
console.log('val의 값은 ' + val + ' 이다.');
  • 힌트 : yield 를 만나면 멈추는 것이지 yield 는 몇번이고 쓸 수 있다.
  • 여기까지 해보았다면 대충 감이 잡혔을 것이다.

gfn을 이중으로 실행해보자!!

function* gfn1(no)
{
	yield no + 1;
	yield no + 2;
	yield no + 3;
}

function* gfn2(no)
{
	var tmp = gfn1(no);

	for (val of tmp)
	{
		yield val;
	}

	yield no + 100;
}

var gen = gfn2(100);

for (val of gen)
{
	console.log(val);
}
  • for (element of array) 는 in 과 달리 프로퍼티가 아닌 iterator 의 값을 차례대로 반환해준다.
  • 자바스크립트에 iterator 를 가진 것은 예를들어 배열이 있다.
  • 이것또한 6버전에서 추가되었다. 결과 101 102 103 200 해설 (생략)
  1. gfn2가 no 즉 100을 인자로 받아 진행된다.
  • var gen = gfn2(100); 선언 뒤 for (val of gen) 구간
  1. var tmp = gfn1(no); 으로 100을 넘겨주고 gfn1로 향한다.
  2. for (val of tmp) 을 통해 gfn1의 모든 yield 를 사용할 때까지 반복한다.
  • 그결과 101 102 103 을 반환한다.
  1. 마찬가지로 200을 반환하면서 gfn2의 모든 yield 를 반환했으며 더이상 다음 iterator 가 없어 정지한다.

gfn을 이중으로 실행해보자!! 2 - yield*

  • 위 예제를 보면 이중으로 쓸때 매우불편하다고 느겼을 것이다.
  • 하지만 yield* 를 사용하면 이 과정을 위임할 수 있다.
function* gfn1(no)
{
	yield no + 1;
	yield no + 2;
	yield no + 3;
}

function* gfn2(no)
{
	// yield* 가  gfn1 을 위임한다.
	yield* gfn1(no);
	yield no + 100;
}

var gen = gfn2(100);

for (val of gen)
{
	console.log(val);
}

해설 위 코드는 그 위 코드와 같은 코드이다. 다만 yield* 을 만난 시점에서 yield* 이 gfn1 를 위임하여 실행한 것이다.

기타

오프젝트나 클래스 {}내부에서는 아래와 같은 문법으로 선언하여 사용할 수 있다.

var temp =
{
	*genFn() { console.log('제네레이터 함수'); yield null; }
};

temp.genFn().next().value;

또한 일반적으로 변수에 선언할 때도 function 과 유사하게 선언 가능하다.

var tempGenFn = function* () { console.log('제네레이터 함수'); yield null; };
tempGenFn().next().value;