제너레이터
코드 블록의 실행을 일시 중지했다가 필요한 시점에 재개할 수 있는 특수한 함수.
제너레이터 함수를 호출하면 제너레이터 객체가 반환된다. (함수 코드가 실행되는 것이 아니다.)
제너레이터 함수의 정의
function* 과 같이 function 뒤에 애스터리스크(*)를 붙여서 함수를 정의한다.
또한 하나의 yield 표현식을 포함한다.
이것을 제외하면 일반 함수를 정의하는 것과 동일하다.
하지만 화살표 함수와 new 연산자를 통한 생성자 함수로는 호출할 수 없다.
// 제너레이터 함수 선언문
function* genDecFunc() {
yield 1;
};
// 제너레이터 함수 표현식
const genExpFunc = function* () {
yield 1;
};
// 제너레이터 메서드
const obj = {
* genObjMethod() {
yield 1;
}
};
class MyClass {
* genClsMethod() {
yield 1;
}
}
제너레이터 객체
제너레이터 함수를 호출하면 일반함수처럼 코드 블록을 실행하는 것이 아니라
제너레이터 객체를 생성해서 반환한다. 제너레이터 함수가 반환한 제너레이터 객체는
이터러블이면서 동시에 이터레이터이다.
제너레이터 객체는 next 메서드를 갖는 이터레이터지만
이터레이터에는 없는 return, throw 메서드를 갖는다.
function* genFunc() {
yield 1;
yield 2;
yield 3;
}
const generator = genFunc();
console.log(Symbol.iterator in generator);
console.log('next' in generator);
console.log('return' in generator);
console.log('throw' in generator);
- next 메서드를 호출하면 제너레이터 함수의 yield 표현식까지 코드 블록을 실행하고 yield된 값을 value 프로퍼티 값으로, false를 done 프로퍼티 값으로 갖는 이터레이터 리절트 객체를 반환한다.
- return 메서드를 호출하면 인수로 전달받은 값을 value 프로퍼티 값으로, true를 done 프로퍼티 값으로 갖는 이터레이터 리절트 객체를 반환한다.
function* genFunc() {
try {
yield '1을 산출(yield)합니다.';
yield '2를 산출(yield)합니다.';
yield '3을 산출(yield)합니다.';
} catch (e) {
console.error(e);
}
}
const generator = genFunc();
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
console.log(generator.return('End!'));
- throw 메서드를 호출하면 인수로 전달받은 에러를 발생시키고 value 값은 undefined, done값은 true가 할당된 이터레이터 리절트 객체를 반환한다.
function* genFunc() {
try {
yield 1;
yield 2;
yield 3;
} catch (e) {
console.error(e);
}
}
const generator = genFunc();
console.log(generator.next());
console.log(generator.throw('Error!'));
제너레이터의 일시 중지와 재개
제너레이터 객체의 next 메서드를 호출하면 제너레이터 함수의 코드블록을 실행한다.
단, yield 표현식까지만 실행하며, yield 키워드는 제너레이터 함수의 실행을 일시 중지시키거나, yield 키워드 뒤에 오는 표현식의 평가 결과를 제너레이터 함수 호출자에게 반환함.
function* genFunc() {
yield 1;
yield 2;
yield 3;
}
// 제너레이터 함수를 호출하면 제너레이터 객체 반환
// 이터러블이면서 동시에 이터레이터인 제너레이터 객체는 next 메서드를 가짐
const generator = genFunc();
// 처음 next 메서드를 호출하면 첫 번째 yield 표현식 까지 실행되고 일시 중지.
// next 메서드는 이터레이터 리절트 객체({value, done}) 반환.
// value 프로퍼티에는 첫 번째 yield 표현식에서 yield(산출)된 값 1이 할당.
// done 프로퍼티에는 제너레이터 함수가 끝까지 실행되었는지를 나타내는 값 할당
// yield가 남았으므로, 여기에서는 false 할당.
console.log(generator.next());
// {value : 2, done : false}
console.log(generator.next());
// {value : 3, done : false}
console.log(generator.next());
// 남은 yield 표현식이 없음
// {value : undefined, done : true}
console.log(generator.next());
- 이터레이터의 next 메서드와 달리 제너레이터 객체의 next 메서드에는 인수를 전달할 수 있다.
- 제너레이터 객체의 next 메서드에 전달한 인수는 제너레이터 함수의 yield 표현식을 할당받는 변수에 할당된다.
- yield 표현식을 할당받는 변수에 yield 표현식의 평가 결과가 할당되지 않는 것에 주의해야 한다.
function* genFunc() {
// 처음 next 메서드를 호출하면 첫 번째 yield 표현식까지 실행되고 일시 중지된다.
// 이때 yield된 값 1은 next 메서드가 반환한 이터레이터 리절트 객체의 value 프로퍼티에 할당된다.
// 그러나 x 변수에는 아무것도 할당되지 않았다. x 변수의 값은 next 메서드가 두 번째 호출될 때 결정된다.
const x = yield 1;
// 두 번째 next 메서드에 호출할 때 전달한 인수 20은 첫 번째 yield 표현식을 할당받는 x 변수에 할당된다.
// 두 번째 next 메서드를 호출하면 두 번째 yield 표현식까지 실행되고 일시 중지된다.
// 이 때 yield된 값 x + 10은 next 메서드가 반환한 이터레이터 리절트 객체의 value 프로퍼티에 할당된다.
const y = yield (x + 10);
// 세 번째 next 메서드에 호출할 때 전달한 인수 40은 두 번째 yield 표현식을 할당받는 y 변수에 할당된다.
// const y = yield (x + 40);은 세 번째 next 메서드를 호출했을 때 완료된다.
// 여기에선 세번째 next 메서드를 호출하면 함수가 끝까지 실행되는 것이다.
// 이 때 제너레이터 함수의 반환값 x + y는 next 메서드가 반환한 이터레이터 리절트 객체의 value 값에 할당된다.
// 따라서 제너레이터는 값을 반환할 필요가 없고 return은 종료의 의미로만 사용해야 한다.
return x + y;
}
const generator = genFunc(0);
let res = generator.next(0)
console.log(res);
res = generator.next(20);
console.log(res);
res = generator.next(40);
console.log(res);
next의 인수는 제너레이터 함수의 yield의 표현식을 할당받는 변수에 할당된다를 주의깊게 생각해보자.
'Javascript' 카테고리의 다른 글
(JS) async / await (0) | 2022.06.13 |
---|---|
(JS) 제너레이터의 활용 (0) | 2022.06.13 |
(JS) 사용자 정의 이터러블 (1) | 2022.06.13 |
(JS) 이터레이터 (0) | 2022.06.11 |
(JS) 이터러블 (아직 직관적인 이해가 안감) (0) | 2022.06.11 |