한 걸음씩 기록하며
[Generator]란 무엇인가 본문
*본 글은 아래의 이터레이션 글과 이어집니다.
https://haksae.tistory.com/134
1. Generator
1) 제너레이터는 ES6에 도입된 함수로서, 이터러블을 생성하는 함수이다.
(당연히 제너레이터로 만든 이터러블은 이터러블 프로토콜과 이터레이터 프로토콜을 따르는 객체이다.)
2) 제너레이터는 제너레이터 함수의 반환으로, 제너레이터의 이터러블에서 반환하는 이터레이터는 자기 자신이다.
- 2)은 MDN의 정의인데, 말이 어렵다.
- 그냥 풀어서 말하면 제너레이터는 실행을 잠시 멈췄다가 나중에 다시 자신에게 접근할 수 있는 함수라고 이해하면 된다.
function* generatorFunction() {
yield 42;
}
const generator = generatorFunction();
generator === generator[Symbol.iterator]();
// 이 말인 즉슨, 제너레이터의 이터러블은 다음과 같은 방식으로 구현되어 있을 거라는 것을 암시한다.
// generator[Symbol.iterator] = () => this;
- 제너레이터 함수는 나중에 자신에게 다시 접근하기 위해서 context(즉 변수값)를 저장된 상태로 남겨둔다.
- 제너레이터는 주로 Promise와 결합하여 사용되며, Callback 지옥 같은 비동기 프로그래밍의 문제점을 많이 완화시켜준다.
2. Generator의 요소
- 제너레이터 함수 선언
- 제너레이터 함수는 함수 키워드 뒤에 *이 붙은 형태로 만들 수 있다.
function* [name]([param1[, param2[, ..., paramN]]]) { // *을 이용해 정의
statements
}
- yield / next
- yield는 제너레이터 함수의 실행을 일시적으로 정지시키고, yield 뒤에 오는 표현식은 제너레이터의 caller에게 반환된다. 즉 일반함수의 return과 매우 유사하다. 그리고 정지(제어)를 재개할 수 있게 해주는 것이 next이다.
- 제너레이터 객체에서 next 메서드를 호출하면 이전 호출 다음에 존재하는 yield까지만 실행된다.
- 즉 yield는 생성된 제너레이터 객체가 반복을 수행하면서 반환 할 값(Iterator Result)을 결정하고, 더 이상 yield가 존재하지 않을 때까지 반복 수행한다.
- next 메서드는 호출하면 {value, done}을 반환하다가, 끝으로 {value: undefined, done: true}를 반환하고 마무리된다.
- 예시
function* someGeneratorFunction() {
console.log('1번 실행')
yield 1
console.log('2번 실행')
yield 2
console.log('3번 실행')
yield 3
}
const iter = someGeneratorFunction()
iter.next() // 1번 실행, { value: 1, done: false }
for (num of iter) console.log(num) // 2번 실행, 2, 3번 실행, 3
- yield*
- yield 키위 뒤에 *를 붙이면 또 다른 이터레이터를 yield 시킬 수 있게 된다. 이를 통해 중첩된 제너레이터 구조를 만들 수 있다.
function* someGeneratorFunction() {
const iter = otherGeneratorFunction()
yield 0
yield* iter
}
function* otherGeneratorFunction() {
yield 1
yield 2
}
const gen = someGeneratorFunction()
gen.next() // { value: 0, done: false }
gen.next() // { value: 1, done: false }
gen.next() // { value: 2, done: false }
gen.next() // { value: undefined, done: true }
- return을 사용하면?
- 제너레이터 함수 내부에서 return을 실행하거나, 제너레이터 인스턴스에서 return 메서드를 실행하면 값이 반환되며, done 값이 true로 변경된다. 즉 반복이 멈춘다.
- 주의할 점은 for of 전개 연산자 등에서는 return 키워드가 나오기 전까지의 yield만 취급한다.
function* gen() {
yield 1
return 2
yield 3
}
const iter = gen()
iter.next() // { value: 1, done: false }
iter.next() // { value: 2, done: true }
iter.next() // { value: undefined, done: true }
const iter2 = gen()
for (let num of iter2) console.log(num) // 1 <-- 2는 출력되지 않는다.
- throw를 사용하면?
- 제너레이터 함수 내부에서 throw를 실행하거나, 제너레이터 인스턴스에서 throw 메서드를 실행하면 Uncaught 에러가 발생하며 done 값이 true로 변경된다.
function* gen() {
yield 1
throw '에러 발생!!'
yield 3
}
const iter = gen()
iter.next() // { value: 1, done: false }
iter.next() // Uncaught 에러 발생!!
iter.next() // { value: undefined, done: true }
3. 제너레이터로 이터러블 구현하기
- 제너레이터 함수를 사용하면 이터레이션 프로토콜을 준수해 이터러블을 생성하는 방식보다 간편하게 이터러블을 구현할 수 있다.
// 무한 이터러블을 생성하는 제너레이터 함수
const createInfiniteFibByGen = function* (max) {
let [prev, curr] = [0, 1];
while (true) {
[prev, curr] = [curr, prev + curr];
if (curr >= max) return; // 제너레이터 함수 종료
yield curr;
}
};
for (const num of createInfiniteFibByGen(10000)) {
console.log(num);
}
참고자료
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Generator
https://poiemaweb.com/es6-generator
https://armadillo-dev.github.io/javascript/what-is-generator/
https://bbaktaeho-95.tistory.com/79
https://im-developer.tistory.com/193
https://wonism.github.io/javascript-generator/
'JavaScript & Node.js' 카테고리의 다른 글
[MongoDB 계정 사용하여 권한 부여하기 (feat. Node.js, robo 3T] (0) | 2022.02.15 |
---|---|
[dotenv]란 무엇인가 (0) | 2022.02.12 |
[Iteration, Iterable, Iterator]이란 무엇인가 (0) | 2022.02.09 |
[JSON & Ajax] 개념 간단 정리 (0) | 2022.02.06 |
[NPM과 Package.json]란 무엇인가 (0) | 2022.01.31 |
Comments