제너레이터 함수

제너레이터 함수(generator function)는 여러 값을 시간차를 두고 반환할 수 있는 함수의 한 형태입니다. 일반 함수와 달리 function* 키워드를 사용하여 선언합니다.

예를 들어 1부터 3까지 시간차를 두고 반환하는 제너레이터 함수는 다음과 같습니다.

function* generator1() {
  yield 1
  yield 2
  yield 3
}

yield 키워드는 사용자가 명시적으로 다음 값을 호출할 때 반환할 값 앞에 붙이는 키워드입니다. 그러면 다음값을 호출하는 방법은 무엇일까요? generator() 함수를 실행한 형태에서 뒤에 .next()를 붙이면 yield 값이 순차적으로 반환이 됩니다.

const gen1 = generator1()
console.log(gen1.next(), gen1.next(), gen1.next())

위에 보이는 객체는 IteratorResult 타입의 객체이며, valueyield 값이고, done은 제너레이터 순회가 종료되었는지를 나타내는 변수입니다. 지금은 명시적으로 제너레이터 순회 종료를 하지 않았으므로 done이 모두 false가 됩니다.

제너레이터 객체의 yield 값들은 반복적으로 호출된다는 점에서 iterable한 특성을 지니며, 따라서 for...of 문을 사용할 수 있습니다.

for(let num of generator1()) {
  console.log(num)
}

 

제너레이터 종료

제너레이터 순회 종료를 하는 방법으로 .return()을 사용하는 방법이 있습니다.

const gen1 = generator1()
console.log(gen1.next(), gen1.return(-Infinity), gen1.next())

두 번째에 .next() 대신 .return()을 사용하면 제너레이터가 강제 종료되며, donetrue로 바뀝니다. 이후에는 다시 .next()를 호출해도 3이 나오지 않습니다. .return()의 괄호 안에 값을 넣으면 해당 값이 value에 강제 배정되며, 아무것도 넣지 않으면 valueundefined가 할당됩니다.

또는 제너레이터 함수의 내부에 yield 대신 return 키워드를 사용하면 그 부분이 종료 시점이 됩니다. 만약 return 3이 마지막줄에 있다면, 마지막 .next() 호출 시 donetrue인 IteratorResult가 반환됩니다. 참고로 for...of문 사용시 return 값은 반환되지 않습니다. 즉, 12만 나오며 종료됩니다.

 

무한한 제너레이터

다음은 숫자 아이디를 생성하는 제너레이터 함수입니다.

function* idMaker(){
    let index = 0;
    while(true)
        yield index++;
}
const idm = idMaker()
console.log(idm.next(), idm.next()) // 1, 2

주의할 점은 여기서도 for...of 문을 사용할 수 있는데, 이 제너레이터 함수에는 종료 조건이 없으므로 무한한 next() 가 가능하며, 따라서 for...of문을 실행하면 끝없이 아이디가 생성되어 무한 루프에빠지는 문제가 발생합니다.

 

예외 발생

..throw()를 발생하면 제너레이터 함수 순회 중 강제로 예외를 발생시킬 수 있습니다.

function* generator2(startNum = 15) {
  try {
    while (true) {
      yield startNum++
    }
  } catch (err) {
    return err
  }
}
const gen2 = generator2(20)
console.log(gen2.next(), gen2.next())
console.log(gen2.throw("err"), gen2.next())

throw 블록에서 return 대신 yield를 사용할 수도 있으며, 이 경우 .next()가 계속 진행됩니다.

 

yield* 키워드를 사용하면, iterable한 배열 등도 yield 목록에 포함시킬 수 있습니다.

function* generator3() {
  yield "A"
  yield "B"
  yield* ["C", "D", "E"]
  yield "F"
}
for(let chr of generator3()) {
  console.log(chr)
}

중간의 ["C", "D", "E"] 배열을 yield* 키워드로 배정하면 각 원소 순서대로 "C", "D", "E".next()를 통해 호출될 수 있음을 알 수 있습니다.

 

출처: JavaScript Generator 이해하기


문의 | 코멘트 또는 yoonbumtae@gmail.com  donaricano-btn

카테고리: WEB: Frontend

답글 남기기

이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다