단 한 번의 실행으로 함수의 끝까지 실행이 완료되는 일반 함수와는 달리, 제너레이터 함수는 사용자의 요구에 따라 (yield와 next를 통해) 일시적으로 정지될 수도 있고, 다시 시작될 수도 있다. 또한, 제너레이터 함수의 반환으로는 제너레이터가 반환된다.
Generator / yield
Generator는 이 제너레이터 함수의 반환으로 iterable 프로토콜과 iterator 프로토콜을 따르는 객체이다.
이 때, 제너레이터의 이터러블에서 반환하는 이터레이터는 자기 자신이다.
function* generator(params) {
yield 'hello';
yield 'world';
// statements
return 'hi';
}
const resultGenerator = generator();
console.log(resultGenerator); // { [Iterator] } -> generator object
- Generator 함수는 일반적인 함수와 달리 function 뒤에 *를 붙여서 정의한다.
- Generator 함수는 화살표 함수를 이용해 정의 할 수 없다.
- Generator 함수는 호출되더라도 함수의 body를 즉시 실행하지 않는다. 대신 함수의 iterator 객체를 반환한다.
- 함수 내부에 yield라는 키워드가 사용된다.
- yield는 제너레이터 함수의 실행을 일시적으로 정지시키며 yield 뒤에 오는 표현식은 제너레이터의 caller에게 반환된다. 즉, yield는 일반 함수의 return과 매우 유사하다고 볼 수 있다. <but, 제너레이터 함수에서 return은 다른 역할을 가짐. 아래 참조>
- yield는 생성된 Generator 객체가 반복을 수행하면서 반환할 값(IteratorResult)을 결정한다.
console.log(resultGenerator.next()); // { value: 'hello', done: false } -> still in suspender state
console.log(resultGenerator.next()); // { value: 'world', done: false } -> still in suspender state
console.log(resultGenerator.next()); // { value: 'hi', done: true } -> finished
console.log(resultGenerator.next()); // { value: undefined, done: true } -> nothing to return here
- 반환된 iterator 객체의 next() 메소드가 호출되면 Generator 함수의 body가 yield expression이 나타날때까지 실행된다.
- 더이상 yield가 존재하지 않을 때까지 반복을 수행하고, 그 이후에 next 메서드를 호출하면 { value: undefined, done: true }를 반환한다.
yield*
yield 키워드 뒤에 *를 붙이면 또 다른 Iterator를 yield 시킬 수 있게 된다. 이를 통해 중첩된 Generator 구조를 만들 수 있다.
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 뒤에 오는 값은 IteratorResult 객체의 value 프로퍼티에 할당되며, 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는 출력되지 않는다.
- return메소드가 호출되었을 때 제너레이터 함수의 코드가 try / finally 안에 있으면, 시퀸스가 종료되지 않는다.
throw
Generator 함수 내부에서 throw를 실행하거나, Generator 인스턴스에서 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 }
Generator 특징
1. 문장을 값으로 전환
- Generator를 이용하면 문장을 값으로 만들 수 있다. 이를 통해 프로그래머가 원하는 어떠한 값이든 반복이 가능하게 만들 수 있다.
- 아래는 원하는 범위 내에서 홀수 값만 입력 받는 예제이다. Generator 함수 내에 표현된 문장(for문)을 이용해 100까지 존재하는 홀수 iterator 값으로 전환한다.
function* odds(num) {
for(let i = 1; i < num; i += 2) {
yield i
}
}
const iter = odds(100)
for (num of iter) console.log(num)
// 1,3,5,7,9...97,99
2. 지연평가(Lazy evaluation)
- Generator는 어떠한 값에 대하여 평가하는 시점을 사용자가 선택할 수 있다.
- 아래 코드에서 사용자는 generator 함수를 실행했지만, 로그에는 아무런 내용도 출력되지 않는다. iter.next()를 실행해야 로그에 "반갑습니다." 영역이 출력된다.
- 자바스크립트에서는 Generator의 이러한 특징을 이용해 지연 평가를 구현 할 수 있다.
function* generator(){
console.log('반갑습니다.') //iter.next()를 실행해야 출력된다.
yield 1
yield 2
yield 3
}
const iter = generator()
//iter.next()
'Javascript' 카테고리의 다른 글
[Javascript] 자료구조 (Data Structure) (0) | 2023.01.22 |
---|---|
[Javascript] Lazy evaluation(지연 평가) (0) | 2023.01.19 |
[Javascript] 커링(Currying)이란 (0) | 2023.01.17 |
[Javascript] require 와 import 비교 (0) | 2023.01.09 |
[JS] 중복되지 않는 알파벳으로 이루어진 가장 긴 문자열 찾기 (0) | 2022.12.31 |