제너레이터 generator

Gangsan O·2022년 5월 20일
0

generator 이해

선행 지식

정의

  • 코드 블록의 실행을 일시 중지 했다가 필요한 시점에 재개할 수 있는 특수한 함수

특징 | 일반 함수와의 차이점

  1. 함수 호출자에게 함수 실행의 제어권을 양도할 수 있음
  2. 함수 호출자와 함수의 상태를 주고 받을 수 있음
  3. 함수 호출 시 제너레이터 객체를 반환함

사용처

  1. 이터러블의 구현
    • 이터레이션 프로토콜을 준수해 직접 이터러블을 생성하는 방식보다 간단하게 이터러블 구현 가능
  2. 비동기 처리
    • 프로미스의 후속 처리 메서드 then/catch/finally 없이 비동기 처리 결과를 반환하도록 구현 가능
    • ES6에서 도입되었지만 ES8에서 나온 async/await로 대체되어 현재에는 잘 쓰이지 않음
    • 하지만 실행 중에도 제너레이터 호출 코드와 데이터를 교환할 수 있기 때문에 유용한 경우가 존재, 이터러블 객체를 쉽게 만들 수 있다는 장점도 아직 유효함

제너레이터 객체

  • 제너레이터 객체는 이터러블이면서 이터레이터임
    • Symbol.iterator 메서드를 호출해서 따로 이터레이터를 만들 필요 없음
  • 이터레이터가 가지고 있지 않은 return과 throw 메서드를 추가로 가짐
  • next 메서드 호출 시
    • 제너레이터 함수의 yield 표현식까지 코드 블록을 실행
    • 다음의 값을 갖는 객체를 반환
      • value 프로퍼티에 yield된 값
      • done 프로퍼티에 함수 종료 여부(boolean)
  • return 메서드 호출 시
    • 다음의 값을 갖는 객체를 반환
      • value 프로퍼티에 인수로 전달받은 값
      • done 프로퍼티에 true
  • throw 메서드 호출 시
    • 인수로 전달 받은 에러를 발생 시키고 다음의 값을 갖는 객체를 반환
      • value 프로퍼티에 undefined
      • done 프로퍼티에 true

yield?

  • 생산하다, 산출하다 / 양도하다의 뜻
  • 실행을 멈추고 산출하고자 하는 값인 value를 바깥 함수에 반환
  • 함수 실행의 제어권을 바깥 함수에 양도

작동 예시

function* gen() {
  let ask1 = yield "2 + 2 = ?";

  alert(ask1); // 4

  let ask2 = yield "3 * 3 = ?"

  alert(ask2); // 9
}

let generator = gen();

alert( generator.next().value ); // "2 + 2 = ?"

alert( generator.next(4).value ); // "3 * 3 = ?"

alert( generator.next(9).done ); // true

generator 활용

이터러블 구현

  • generator 사용 없이 직접 이터러블을 구현하려면 이터러블 프로토콜을 직접 만들어야 함
// without generator
const infiniteFibonacci = (function () {
  let [pre, cur] = [0, 1];

  return {
    [Symbol.iterator]() {
      return this;
    },
    next() {
      [pre, cur] = [cur, pre + cur];
      return { value: cur };
    },
  };
})();

for (const num of infiniteFibonacci) {
  if (num > 10000) break;
  console.log(num);
}
  • generator를 활용한 코드
const infiniteFibonacci = (function* () {
  let [pre, cur] = [0, 1];

  while (true) {
    [pre, cur] = [cur, pre + cur];
    yield cur;
  }
})();

for (const num of infiniteFibonacci) {
  if (num > 10000) break;
  console.log(num);
}

비동기 처리

  • next메서드와 yield 표현식을 통해 함수 호출자와 함수의 상태를 주고 받을 수 있음
// const fetch = require("node-fetch"); // for Node.js environment

const async = generatorFunc => {
  const generator = generatorFunc();

  const onResolved = arg => {
    const result = generator.next(arg);
    return result.done
      ? result.value
      : result.value.then(res => onResolved(res));
  };
  return onResolved;
};

async(function* fetchTOdo() {
  const URL = "https://jsonplaceholder.typicode.com/todos/1";

  const response = yield fetch(URL);
  const todo = yield response.json();
  console.log(todo);
})();
  • ES8에 나온 async/await로 제너레이터보다 간단하고 가독성 좋게 비동기 처리 가능
// const fetch = require("node-fetch"); // for Node.js environment

async function fetchTodo() {
  const URL = "https://jsonplaceholder.typicode.com/todos/1";

  const response = await fetch(URL);
  const todo = await response.json();
  console.log(todo);
}

참고자료

제너레이터와 비동기 이터레이션 - 모던 javascript 튜토리얼
[책] 자바스크립트DeepDive - 이웅모 저

profile
감동 코딩

0개의 댓글