JavaScript | Generator

Kate Jung·2022년 1월 23일
0

JavaScript

목록 보기
30/39
post-thumbnail

📌 Generator란?

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

  • 즉, 다른 작업을 하다가 다시 돌아와서 next()해주면 진행이 멈췄던 부분부터 이어서 실행

  • Redux-saga에서 활발히 사용

📌 기본 문법

  • function*

  • yield

    • 내부에서 사용

    • 함수의 실행을 멈출 수 있음

  • 예시 코드

    function* fn(){
      yield 1
      yield 2
      yield 3
      return "finish"
    }
    
    let a = fn() // Generator 함수 실행

📌 Generator 객체

실행을 처리하는 특별 객체

  • Generator 실행 시 → Generator 객체가 반환됨(코드 실행 x)

  • 가지고 있는 메소드

    next(), return(), throw()

🔹 next()

가장 가까운 yield문을 만날 때까지 실행 & 데이터 객체를 반환

◾ 반환된 데이터 객체

value & done 프로퍼티를 가짐

  • value

    • yield 오른쪽에 있는 값
    • 값 생략 시 → undefined
  • done

    • 함수 코드가 끝났는지 나타냄

    • 실행 끝 → true

      아니면 → false

◾ 예시 코드

function* fn(){
  console.log(1)
  yield 1
  console.log(2)
  yield 2
  console.log(3)
  console.log(4)
  yield 3
  return "finish"
}

let a = fn()

console.log(a) // Generator 함수 실행
// Generator 객체만 반환. 함수 본문 코드: 실행x.

console.log(a.next()) // 첫 번째 yield 까지 실행
// 1
// {value: 1, done: false}

console.log(a.next()) // 두 번째 yield 까지 실행
// 2
// {value: 2, done: false}

console.log(a.next()) // 세 번째 yield 까지 실행
// 3
// 4
// {value: 3, done: false}

console.log(a.next()) // return까지 실행
// {value: "finish", done: true}

console.log(a.next())
// {value: undefined, done: true}
// value는 표현 할 것이 없음. done은 계속 true

◾ 인수 전달 가능

외부로부터 값을 입력 받을 수 있다.

function* fn() {
  let num1 = yield "첫 번째 숫자를 입력해주세요."
  console.log(num1)

  let num2 = yield "두 번째 숫자를 입력해주세요."
  console.log(num2)

  return num1 + num2
}

const a = fn()

console.log(a.next())
// 첫 번째 yield에서 멈춤.
// value는 yield 오른쪽에 있는 값.
// 결과 : {value: "첫 번째 숫자를 입력해주세요.", done: false}

console.log(a.next(2)) 
// 인수 넣음. 인수로 넣은 값은 num1에 저장됨.
// 그 다음 콘솔로 보여짐.
// 두 번째 yield에서 멈춤.
// 결과 : 2 {value: "두 번째 숫자를 입력해주세요.", done: false}

console.log(a.next(4))
// num2에 인수 값 4가 들어감.
// console.log로 보여짐.
// 더이상 yield 없음 -> done이 true가 됨.
// value는 두 숫자를 더한 값이 됨.
// 결과 : 4 {value: 6, done: true}

🔹 return()

호출 즉시 done 속성 값은 true & 전달하는 인수가 있으면 value 값이 됨.

  • 이후 next 실행 시

    • value → 못 얻음
    • donetrue
  • 예시 코드

    function* fn(){
      console.log(1)
      yield 1
      console.log(2)
      yield 2
      console.log(3)
      console.log(4)
      yield 3
      return "finish"
    }
    
    let a = fn()
    
    console.log(a.next())
    // 1
    // {value: 1, done: false}
    
    console.log(a.next())
    // 2
    // {value: 2, done: false}
    
    console.log(a.return('END'))
    // {value: "END", done: true}
    
    console.log(a.next())
    // {value: undefined, done: true}

🔹 throw()

호출 즉시 done 속성 값은 true & catch문 내부 실행

  • 이후 next 실행 시

    • value → 못 얻음
    • donetrue
  • 예시 코드

    function* fn(){
      // 예외 처리를 위해서 try cath 문으로 코드를 감쌈.
      try {
        console.log(1)
        yield 1
        console.log(2)
        yield 2
        console.log(3)
        console.log(4)
        yield 3
        return "finish"
      } catch(e) {
        console.log(e)
      }
    }
    
    let a = fn()
    
    console.log(a.next())
    // 1
    // {value: 1, done: false}
    
    console.log(a.next())
    // 2
    // {value: 2, done: false}
    
    console.log(a.throw(new Error('err')))
    // Error: err (에러 로그 찍힘)
    // {value: undefined, done: true}
    
    console.log(a.next())
    // {value: undefined, done: true}
    // value: undefined ->  아무 값도 받을 수 없음
    // done: true -> 이미 끝남

📌 iterable

Generatoriterator 이면서 iterable 이다.

🔹 의미

반복이 가능하다

🔹 조건

  • Symbol.iterator 메서드가 구현되어 있어야 함.
  • Symbol.iteratoriterator 를 반환해야 함.

🔹 iterable (반복 가능한) 객체인 것

◾ 확인 방법 및 이유

Symbol.iterator가 있고 이 메서드가 반환하는 값이 iterator 이므로 iterable 하다고 할 수 있음.

◾ 배열

  • 배열도 반복 가능

  • 확인

    • 방법

      배열의 prototype 을 보면 Symbol.iterator가 있음. 이걸 활용.


    • 예시 코드

        const arr =[1,2,3,4,5]
        
        let it = arr[Symbol.iterator](); // it 변수 안에 array가 가진 Symbol.iterator 메소드를 실행한 값을 넣음.
        
        console.log(it.next()) // {value: 1, done: false}
        console.log(it.next()) // {value: 2, done: false}
        console.log(it.next()) // {value: 3, done: false}
        console.log(it.next()) // {value: 4, done: false}
        console.log(it.next()) // {value: 5, done: false}
        console.log(it.next()) // {value: undefined, done: true}

◾ Generator

  • 이유

    GeneratorSymbol.iterator메소드를 실행한 값 === 자기 자신

    function* fn(){
      yield 4
      yield 5
      yield 6
    }
    
    const a = fn()
    
    console.log(a[Symbol.iterator]()===a) // true
    // 의미: Generator에 Symbol.iterator메소드를 실행한 값이 자기 자신이다.

◾ 문자열

  • 확인
    const str = 'hello';
    
    // Symbol.iterator 유무 확인
    console.log(str[Symbol.iterator]) // 존재: ƒ [Symbol.iterator]() {} 
    
    // next() 실행
    const xx = str[Symbol.iterator]();
    console.log(xx.next()) // {value: "h", done: false}
    console.log(xx.next()) // {value: "e", done: false}
    console.log(xx.next()) // {value: "l", done: false}
    console.log(xx.next()) // {value: "l", done: false}
    console.log(xx.next()) // {value: "o", done: false}
    console.log(xx.next()) // {value: undefined, done: true}

🔹 순회 가능(for of 이용)

◾ 동작 원리

  1. for .. of가 시작이 되면 Symbol.iterator 를 호출.

    만약 없으면 → 에러 발생

  2. 반환된 iteratornext 메소드를 호출하면서 donetrue가 될 때까지 반복

◾ 예시 코드

배열

    const arr =[1,2,3,4,5]
    
    for(let num of arr){
      console.log(num)
    }
    /*
    1
    2
    3
    4
    5
    */

Generator

    function* fn(){
      yield 4
      yield 5
      yield 6
    }
    
    const a = fn()
    
    for(let num of a){
      console.log(num)
    }
    /*
    4
    5
    6
    */

문자열

    const str = 'hello';
    
    const xx = str[Symbol.iterator]();
    
    // for..of 확인
    for(let s of xx){
      console.log(s)
    }
    /*
    h 
    e 
    l
    l 
    o 
    */

📌 iterator

Symbol.iterator 를 호출한 결과

  • next 메서드가 있어야 함.

    next 메서드 반환: valuedone 속성을 가진 객체

  • 작업이 끝나면 → donetrue

📌 [ 특징 ] 값 미리 제작 x

필요한 값만 그때그때 생성

  • 메모리 관리 → 효율적

🔹 브라우저에서 무한 반복자 사용 가능

  • 무한 반복자(while (true)문) 사용해도 브라우저 안 뻗음

  • 이유

    next를 호출 할 때마다 값을 줘서.

  • 예시 코드

    function* fn(){
      let index = 0;
      while(true){
        yield index++
      }
    }
    
    const a = fn()
    console.log(a.next()) // {value: 0, done: false}
    console.log(a.next()) // {value: 1, done: false}
    console.log(a.next()) // {value: 2, done: false}
    console.log(a.next()) // {value: 3, done: false}
  • 설명

    Generator함수를 사용하지 않았다면 break없는 while true문 → 사용하면 안됨.

🔹 일반적인 함수와 비교

  • 일반적인 함수

    값 구할 때, 모든 값을 미리 계산해놓고 유지 해야 함. (쓸지 안 쓸지 정해지지 않은 상황에서도)

  • Generator

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

📌 yield*로 다른 Generator 부르기

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

function* gen2() {
  yield "Hello"
  yield* gen1() // yield*을 하고 다른 제너레이터 함수(gen1())를 호출
  yield "!"
}

console.log(...gen2());
// [결과] Hello w o r l d ! 

🔹 ... (구조 분해 할당)

donetrue가 될 때까지 값을 펼쳐주는 역할

🔹 yield*

다른 generator 또는 이터러블(iterable) 객체에 yield를 위임할 때 사용

즉, 반복 가능한 모든 객체가 올 수 있음.


참고

  • 코딩앙마_자바스크립트 중급
profile
복습 목적 블로그 입니다.

0개의 댓글