Generator function

Whoyoung90·2021년 8월 8일
0
post-thumbnail

> Iterable

반복 가능한 객체(iterable object)는 for...of 구문과 함께 ES2015에서 도입되었다.

반복 가능한 객체를 다른 객체와 구분짓는 특징은
객체의 Symbol.iterator 속성에 특별한 형태의 함수가 들어있다는 것!

let foo = "hello world";
console.log(foo[Symbol.iterator]); // ƒ [Symbol.iterator]()

객체의 Symbol.iterator 속성에 특정 형태의 함수가 들어있다면, 이를 반복 가능한 객체(iterable object) 혹은 줄여서 iterable이라 부르고
"해당 객체는 iterable protocol을 만족한다"고 말합니다.

✅ iterable 객체를 만들어내는 내장된 생성자

  • String
  • Array
  • TypedArray
  • Map
  • Set

✅ 어떤 객체가 Iterable이라면, 그 객체에 대해서 아래의 기능들을 사용할 수 있다.

  • for...of 루프
  • spread 연산자 (...)
  • 분해대입(destructuring assignment)
  • 기타 iterable을 인수로 받는 함수

> Generator Function

Iterable을 구현하는 가장 쉬운 방법은 ES2015에 도입된 generator 함수를 사용하는 것!

Generator 함수는 iterable 객체를 반환하는 특별한 형태의 함수이다.

// generator 함수 선언하기
function* foo1() {
  // ...
}

// 표현식으로 사용하기
const foo2 = function* () {
  // ...
}

// 메소드 문법으로 사용하기
const obj = {
  * foo3() {
    // ...
  }
}

Generator 함수를 호출하면 객체가 생성되는데, 이 객체는 iterable protocol을 만족한다.
즉, Symbol.iterator 속성을 갖고 있다는 뜻이다.

function* foo1() {};
let iterable = foo1();
console.log(iterable[Symbol.iterator]) // ƒ [Symbol.iterator]()

Generator 함수 안에서는 yield라는 특별한 키워드가 있다.
Generator 함수 안에서 yield 키워드는 return과 유사한 역할이며, iterable의 기능을 사용할 때 yield 키워드 뒤에 있는 값들을 순서대로 넘겨준다.

function* foo4() {
  yield 1;
  yield 2;
  yield 3;
}

// 1, 2, 3이 순서대로 출력.
for (let n of foo4()) {
  console.log(n);
}

yield* 표현식을 사용하면, 다른 generator 함수에서 넘겨준 값을 대신 넘겨줄 수도 있다.

function* foo4() {
  yield 1;
  yield 2;
  yield 3;
}

function* foo5() {
  yield* foo4();
  yield* foo4();
}

// 1, 2, 3, 1, 2, 3이 순서대로 출력.
for (let n of foo5()) {
  console.log(n);
}

> Why Generator ?

그렇다면 왜 제네레이터를 사용하는가?

그 이유는 바로 느긋한 평가(lazy evaluation) 때문이다.

프로그래밍에서 느긋한(lazy)이라는 단어는 대개 어플리케이션의 성능에 대해 긍정적인 효과를 가지고 있다.

보통 함수를 호출하면 즉시 함수가 실행되고 종료된다.

function normalFunc(num){
  const arr = [];
  for(let i=0; i<num; i++){
    arr.push(i);
  }
  return arr;
}

console.log(normalFunc(3)); // [0, 1, 2]

하지만 제네레이터 함수를 호출하면

  • 제네레이터 객체를 반환하고,
  • 제네레이터 객체는 함수와 yield문을 조합해서
  • 일시정지/재개 컨트롤을 할 수 있다.
function* generator(num){
  const arr = [];
  for(let i=0; i<num; i++){
    arr.push(i);
    yield arr;
  }
}

const gen = generator(3);
console.log(gen); // Iterator [Generator] {}

gen.next(); // { value: [ 0 ], done: false }
gen.next(); // { value: [ 0, 1 ], done: false }
gen.next(); // { value: [ 0, 1, 2 ], done: false }

코드를 보면
💡 normalFunc()는 호출되면 인자로 받은 숫자만큼의 길이의 배열을 즉시 생성해서 반환하고,
💡 generator()는 호출되면 제네레이터 객체를 반환하고, 제네레이터 객체의 next()를 호출할 때마다 길이가 1씩 늘어나는 배열을 반환한다.

그래서 어디에 쓰이며 어느 부분에서 메모리 효율이 좋아지는 걸까?
인스타그램이나 페이스북은 스크롤을 페이지의 끝까지 내리면
계속해서 게시글들이 로드되어 화면에 보여지는데,

제네레이터 안에 while(true)같은 무한 반복문 내에 데이터 요청-렌더링 코드를 넣어두고, 페이지가 끝나면 next()를 사용해 데이터를 요청하고 렌더링 해주는 것이다.(참고)

[ 출처1 URL ]
[ 출처2 URL ]

profile
비전공으로 일식 쉐프가 되었듯, 배움에 겸손한 프론트엔드 개발자가 되겠습니다 :)

0개의 댓글