18. Generator

조뮁·2022년 8월 11일
0

JS중급

목록 보기
18/18
post-thumbnail

Generator

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

  • 다른 작업을 하다가 다시 돌아와서 next() 해주면 멈췄던 부분부터 이어서 실행 가능함
  • function 옆에 *을 붙여서 생성
  • 내부에 yield 키워드 사용
    - yield 에서 함수의 실행을 멈출 수 있음
  • generator 함수 실행하면 generator 객체 반환

next()

  • generator 객체는 .next() 메소드를 사용하여 가장 가까운 yield문을 만날때까지 코드 실행
function* fn(){
    yield 1;
    yield 2;
    yield 3;
    return 'finish';
}

const a = fn();

a;  // fn {<suspended>}  
// generator 객체  반환되고, 내부 코드는 실행되지 않음
a.next();
// {value: 1, done: false}
a.next();
// {value: 2, done: false}
a.next();
// {value: 3, done: false}
a.next();
// {value: 'finish', done: true}
  • 반환된 data 객체는 value, done 프로퍼티를 가짐
    • value : yield 오른쪽 값 (값 없을 시 undefined)
    • done : 함수 실행이 완전히 끝났으면 true, 아니면 false

return()

  • return 메소드 호출 시, 그 즉시 done : true가 되면서 종료됨.
function* fn(){
    yield 1;
    yield 2;
    yield 3;
    return 'finish';
}

const a = fn();

a.next()
// {value: 1, done: false}
a.return('end');
// {value: 'end', done: true}
a.next()
// {value: undefined, done: true}

throw()

  • throw 메소드 호출 시, generator 함수의 실행을 즉시 끝내고 catch문의 내용 실행
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'));
/*
VM417:8 Error: err
    at <anonymous>:1:9
{value: undefined, done: true}
*/
a.next()
// {value: undefined, done: true}


Generator : iterable임

iterable : 반복 가능한 객체

iterable 조건

  • Symbol.iterator 메서드가 있어야 함
  • Symbol.iterator는 iterator를 반환해야 함

iterator : 메소드를 호출한 결과

iterator 조건

  • iterator는 value와 done 프로퍼티를 가진 객체를 반환하는 메서드 next()가 있어야함.

generator : iterator 이면서 iterable임


배열도 반복이 가능한데, iterable 일까??

배열을 만든 후 출력해보면, Symbol.iterator라는 메소드를 가지고 있다.

arr
// (5) [1, 2, 3, 4, 5]

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}
  • 배열은 Symbol.iterator라는 메소드를 가지고 있고, 이 메소드가 반환하는 값이 iterator이므로, iterable하다고 할 수 있다.
    즉, 배열은 반복 가능한 객체

  • iterable --> for ... of 를 이용하여 순회 가능

for(let num of arr){
    console.log(num)
}
// 1
// 2
// 3
// 4
// 5

배열 = iterable임



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

const a = fn();

a[Symbol.iterator]() === a;  // true
  • generator의 Symbol.iterator()를 실행한 값 === 자기 자신임
    즉, generator === iterable 객체임
// generator는 iterable 객체이기 때문에 for...of 사용 가능

for (let num of a){
    console.log(num);
}
// 3
// 4
// 5
  • for of가 실행되면 symbol.iterator를 호출하고, 만약 Symbol.iterator가 없다면 error 발생
  • 반환된 iterator에 next메소드를 호출하며 done === true 가 될 때까지 반복

문자열 = iterable임

const str = 'hello';

str[Symbol.iterator]
// ƒ [Symbol.iterator]() { [native code] }

const strng = str[Symbol.iterator]();
for (let s of strng) {
    console.log(s);
}
// h
// e
// l
// l
// o


strng.next()
// {value: 'h', done: false}
strng.next()
// {value: 'e', done: false}

// next() 메소드를 사용하는 것도 가능


next() 인수 전달

  • generator는 외부로부터 값은 입력받을 수 있음.
function* fn(){
  const num1 = yield '첫 번째 숫자를 입력해주세요';
  console.log(num1);
  
  const num2 = yield '두 번째 숫자를 입력해주세요';
  console.log(num2);
  
  return num1 + num2;
}

const a = fn();

a.next(3);
// {value: '첫 번째 숫자를 입력해주세요', done: false}
// value 값 : yield 옆에 있는 값
a.next(1);
// 1
// next에 인수를 넣고 실행하면, 해당 값은 num1에 저장됨. 해당 값은 console.log()를 통해 보여짐
//{value: '두 번째 숫자를 입력해주세요', done: false}
a.next(5);
// 5
// {value: 6, done: true}
// 더 이상 yield가 없이 때문에 done: true로 변경됨
a.next();
// {value: undefined, done: true}

generator 메모리 할당

  • generator 는 값을 미리 만들어 놓지 않고, 필요할 때마다 생성한다.
    다음과 같이 무한반복문을 호출해도 괜찮음.
function* fn(){
     let num = 0;
     while(1){
         yield num++;
     }
 }

const a = fn();
a.next();
// {value: 0, done: false}
a.next();
// {value: 1, done: false}
a.next();
// {value: 2, done: false}

yield*

  • 다른 generator 호출 가능
  • yield* 오른쪽에는 반복가능한 모든 객체가 올 수 있음
function* gen1(){
  yield 'w';
  yield 'o';
  yield 'r';
  yield 'l';
  yield 'd';
}

function* gen2(){
  yield 'Hello, ';
  yield* gen1();  // 다른 generator 함수 호출
  yield '!';
}

const g = gen2();
g.next();
// {value: 'Hello, ', done: false}
g.next();
// {value: 'w', done: false}
g.next();
// {value: 'o', done: false}
g.next();
// {value: 'r', done: false}
g.next();
// {value: 'l', done: false}
g.next();
// {value: 'd', done: false}
g.next();
// {value: '!', done: false}
g.next();
// {value: undefined, done: true}

또는, 구조분해할당을 이용하여 다음과 같이 호출 가능

  • for of 와 마찬가지로 done = true가 될 때 까지 값을 펼쳐주는 역할을 함
console.log(...gen2());
// Hello,  w o r l d !

0개의 댓글