[JS] 제너레이터

흠냐뤼·2021년 8월 15일
0

데브코스

목록 보기
4/5

❓ 제너레이터(Generator)란

  • 이터레이터이자 이터러블을 생성하는 함수
  • 제너레이터를 통해 여러 개의 값을 필요에 따라 하나씩 반환할 수 있음
  • 일반 함수처럼 함수의 코드를 한 번에 실행하는 것이 아니라 실행을 멈추었다가 필요한 시점에 재시작할 수 있는 특수한 함수
  • 일반 함수는 return문으로 값을 반환하지만 제너레이터는 호출하면 제너레이터(이터러블이면서 이터레이터인 객체 => Symbol.iterator 메소드를 소유한 이터러블)를 반환

❓ 제너레이터 함수 정의와 구조

  • function* 키워드로 선언

  • 하나 이상의 yield문 포함

  • 제너레이터 객체 메소드 : next, return, throw

① next 메소드

  • 호출하면 value와 done 프로퍼티를 가지는 iterator result 객체를 반환

  • value : 산출 값

  • done : 코드 실행이 끝났으면 true, 끝나지 않았으면 false

  • next 메소드를 호출하면 가장 가까운 yield 문을 만날 때까지 실행된다. yield 문을 만나면 실행을 멈추고(suspend) value를 반환한다.

  • 또 다시 next 메소드를 호출하면 멈춘 위치에서 다시 실행하여 다음 yield 문을 만날 때까지 실행된다.

  • => yield : 함수 실행이 일시적으로 멈추는 위치

[⬆예시 코드]

function* genFunc() {
    console.log("첫번째 호출"); 
    yield 1;  //첫번째 호출 시에는 이 지점까지만 실행된다.
    console.log("두번째 호출");
    yield 2;  //두번째 호출 시에는 이 지점까지만 실행된다.
    console.log("세번째 호출");
    yield 3;  //세번째 호출 시에는 이 지점까지만 실행된다.
    
    yield 4;
    yield 5;
    yield 6;
}

//호출해도 제너레이터 객체를 생성하기만 하고 함수의 코드는 실행되지 않는다.
let generator = genFunc();

console.log(generator);         // Object [Generator] {}

//첫번째 호출
console.log(generator.next());  // { value: 1, done: false }

//두번째 호출
console.log(generator.next());  // { value: 2, done: false }

//세번째 호출
console.log(generator.next());  // { value: 3, done: false }

console.log(generator.return('Hi'));  // {value: 'Hi', done: true}

//yield 4, 5, 6이 있음에도 undefined가 반환되는 것을 알 수 있음
console.log(generator.next()); // { value: undefined, done: true } 

  • next 메소드 안에 인자를 넣어 제너레이터 함수 안으로 전달할 수 있다.

[⬆예시 코드]

function* gen(n) {
    let res;
    res = yield n;    // n: 0 ⟸ gen 함수에 전달한 인수
  
    console.log(res); // res: 1 ⟸ 두번째 next 호출 시 전달한 데이터
    res = yield res;
  
    console.log(res); // res: 2 ⟸ 세번째 next 호출 시 전달한 데이터
    res = yield res;
  }
  const generatorObj = gen(0);
  
  console.log(generatorObj.next());  // 제너레이터 함수 시작
  console.log(generatorObj.next(1)); // 제너레이터 객체에 1 전달
  console.log(generatorObj.next(2)); // 제너레이터 객체에 2 전달

  • 첫번째 next 메서드 호출은 제너레이터 함수의 실행이 시작되도록 하는 역할만 수행
  • next 메서드의 인수로 데이터를 전달할 수 있으며, 전달된 인수는 yield 키워드의 결과값으로 받을 수 있음

② return 메소드

  • 호출하면 done의 값이 참이 된다. 이후에 next 메소드를 호출해도 done은 계속 참을 유지한다.
  • 호출하면서 인수를 넣어주면 그 값이 value의 값이 된다. 이후에 next 메소드를 호출하면 value의 값은 undefined가 된다.

③ throw 메소드

  • try-catch문을 사용하여 제너레이터 함수 내부에서 예외 처리
  • throw메소드 호출 시 예외가 발생한 것으로 처리되어 catch문으로 들어감
  • done값이 true로 됨.
function* f1() {
    try {
        console.log('첫번째 호출');
        yield 10;
        console.log('두번째 호출');
        yield 20;
    } catch(e) {
        console.log('f1-catch', e);
    }
}

let generator = f1();
console.log(generator.next()); // {value: 10, done: false}
console.log(generator.throw('에러 발생')); // {value: undefined, done: true}


❓ 제너레이터의 이점

  • 이터레이션 프로토콜을 준수해 이터러블을 생성하는 방식보다 간편하게 이터러블을 구현할 수 있다.
    (이미 next 메소드 코드[Symbol.iterator]() 코드를 내장하고 있기 때문에)

[예시 : 무한 피보나치 수열 함수 구현]

  • next 메소드를 통해 값을 지연 평가할 수 있다.
    (=> 무한 루프를 만들어도 문제가 되지 않는다, 메모리를 효율적으로 사용할 수 있다.)
function* infinity() {
    let i = 0;
    while (true) yield ++i; 
}

const iter = infinity();

console.log(iter.next().value); // 1
console.log(iter.next().value); // 2
console.log(iter.next().value); // 3
//실행해도 문제가 발생하지 않는 것을 알 수 있다.
  • 동시성 프로그래밍이 가능하다.
    (=> next와 yield를 통해 함수 코드의 실행, 정지가 가능하기 때문에 동시성 프로그래밍이 가능해진다)
    (제너레이터 함수는 yield 키워드를 통해서 자발적으로 자신의 실행을 멈춤 / 제너레이터 객체의 next 메서드를 호출해서 제너레이터 함수가 다시 실행되도록 함)

참조

https://velog.io/@rohkorea86/Generator-%ED%95%A8%EC%88%98%EB%A5%BC-%EC%9D%B4%ED%95%B4%ED%95%B4%EB%B3%B4%EC%9E%90-%EC%9D%B4%EB%A1%A0%ED%8E%B8-%EC%99%9C-%EC%A0%9C%EB%84%A4%EB%A0%88%EC%9D%B4%ED%84%B0-%ED%95%A8%EC%88%98%EB%A5%BC-%EC%8D%A8%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80#1-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%8A%B9%EC%84%B1%EC%9D%84-%EB%8F%99%EA%B8%B0%EC%A0%81-%EC%BD%94%EB%93%9C%EB%B0%A9%EC%8B%9D%EC%9C%BC%EB%A1%9C-%EA%B4%80%EB%A6%AC-%ED%95%B4%EC%A4%8D%EB%8B%88%EB%8B%A4

https://ko.javascript.info/generators

https://poiemaweb.com/es6-generator

https://mygumi.tistory.com/370

profile
https://github.com/ono212

0개의 댓글