Study/JavaScript

[JavaScript_study] Generator

갈푸라떼 2022. 3. 23. 20:19

* Generator : 함수의 실행을 중간에 멈췄다가 재개할 수 있는 기능

ex) Redux Saga

 

  > * 을 사용해서 만든다.

  > 내부에 yield를 사용한다.

  > yield에서 함수의 실행을 멈출수있다.

  > next(), return(), throw() 메소드를 가진다.

 

* Generator

> iterable (반복이 가능한)

  - Symbol.iterator 메서드가 있다.

  - Symbol.iterator는 iterator를 반환해야한다.

  - for ... of 를 통해서 순회 가능하다.

 

> iterator (반복자)

  - next 메서드를 가진다.

  - next 메서드는 value와 done속성을 가진 객체를 반환한다.

  - 작업이 끝나면 done는 true가 된다.

function* fn() {
  yield 1;
  yield 2;
  yield 3;
  return "finish";
}

const a = fn();

// next()를 사용하면 가장가까운 yield를 만날때까지 코드를 실행시키고 데이터 객체를 반환해준다. 
// yield를 만나면 멈춘다.
// value는 yield옆에 있는 값이다. 생략하면 undefined가 반환된다.
// done는 함수가 끝났는가 여부이며 함수가 끝나면 true가 반환된다.

a.next(); // {value: 1, done: false}
a.next(); // {value: 2, done: false}
a.next(); // {value: 3, done: false}
a.next(); // {value: 'finish', done: true} // 함수 끝
a.next(); // {value: undefined, done: true} // 함수가 끝나고 값이 없음

 

* return()

 

function* fn() {
  yield 1;
  yield 2;
  yield 3;
  return "finish";
}

const a = fn();

// return()메서드를 사용하면 그 즉시 done : true가 된다.
a.return("END") // {value: 'END', done: true}
a.next() // {value: undefined, done: true} 함수가 종료되었기때문에 undefined가 출력된다.

 

* throw()

 

function* fn() {
  try {
    yield 1;
    yield 2;
    yield 3;
    return "finish";
} catch (e) {
  console.log(e);
  }
}

const a = fn();

a.next(); {value : 1, done : false}
a.throw(new Error('err')) // error로그가 찍히고 value : undefined, done : true가 된다.

 

* array가 가지고있는 iterator확인

  > 배열은 Symbol.iterator메서드를 가지고 있고 이 메서드가 반환하는 값이 iterator이므로 iterable하다고 할 수 있다.

  > 즉, 배열은 반복가능한 객체이다.

  > for ... of를 통해서 순회 가능하다.

const arr = [1, 2, 3, 4, 5]

const it = arr[Symbol.iterator]();

it.next(); // {value : 1, done: false}
it.next(); // {value : 2, done: false}
it.next(); // {value : 3, done: false}
it.next(); // {value : 4, done: false}
it.next(); // {value : 5, done: false}
it.next(); // {value : undefined, done: true}

for (let num of arr) {
  console.log(num)
};

// 1
// 2
// 3
// 4
// 5

 

* iterator예시 코드

 

function* fn() {
  yield 4;
  yield 5;
  yield 6;
}

const a = fn();

a[Symbol.iterator]() === a; // true
// Generator의 Symbol.iterator메서드를 실행한 값이 자기자신이다. (true)
// 즉, Generator는 iterable객체이다.

// for of또한 사용가능하다.
for(let num of a){
  console.log(num);
}

// 4
// 5
// 6

 

* 문자열 iterator

 

const str = 'hello';

str[Symbol.iterator]

const xx = str[Symbol.iterator]

xx.next(); { value : "h", done: false }
xx.next(); { value : "e", done: false }
xx.next(); { value : "l", done: false }
xx.next(); { value : "l", done: false }
xx.next(); { value : "o", done: false }

for(let s of xx) {
  console.log(s);
}

// h
// e
// ②l
// o

 

* next()에 인수 전달 : 외부로부터 값을 입력 받을 수 있다.

function* fn() {
  const num1 = yield "첫번째 숫자를 입력해주세요";
  console.log(num1);
  
  const num2 = yield "두번째 숫자를 입력해주세요";
  console.log(num2);
  
  return num1 + num2;
}

const a = fn();

a.next(); // {value: '첫번째 숫자를 입력해주세요', done: false}
a.next(2); // 2 {value: '두번째 숫자를 입력해주세요', done: false} // 값은 num1에 저장된다.
a.next(4); // 4 {value : 6, done: true} // value에는 num1 + num2값이 반환된다.

 

* Generator은 값을 미리 만들어 두지않는다. // 메모리 관리측에서 효율적이다.

  > while(true)를 사용해서 무한반복을 만들어도 브라우저가 꺼지지않는다.

  > next()를 줄때마다 값을 호출하기 때문이다.

  > 필요한순간까지 계산을 미룰 수 있다.

function* fn() {
  let index = 0;
  while (true) {
    yield index++;
  }
}

const a = fn();

a.next(); // {value: 0, done: false}

 

yield*을 이용

  > for of와 마찬가지고 done : true가 될때까지 값을 펼쳐준다.

  > *옆에는 반복가능한 모든 객체가 옆에 올수있다.

 

function* gen1() {
  yield "W";
  yield "o";
  yield "r";
  yield "l";
  yield "d";
}

function* gen2() {
  yield "Hello,";
  yield* gen1();
  yield "!";
}

console.log(...gen2()); // Hello, W o r l d !