JavaScript/함수

[JavaScript]제너레이터 함수 응용(반복, 비동기, 옵저버)

DevStory 2021. 12. 5.

제너레이터 함수(Generator Function)란?

제너레이터 함수(Generator Function)는 함수가 도중에 중간에 일시 중지한 다음 중지된 위치에서 로직이 실행되는 함수입니다.

 

자세한 사용 방법은 아래 링크를 클릭하여 게시된 포스팅을 확인해주세요.

☞ 제너레이터 함수(Generator Function) 이해하기

 

이번 포스팅에서는 제너레이터 함수를 실제로 응용 및 활용하는 방법에 대해 소개합니다.

 


반복자(Iterables) 구현

제너레이터는 반복 가능한 객체(iterable Object)이므로 반복자(iterator)를 구현할 수 있습니다.

 

다음은 반복자를 사용한 일반 함수입니다.

const iterableObj = {
  [Symbol.iterator]() {
    let step = 0;
    return {
      next() {
        step++;
        if (step === 1) {
          return { value: 'Iterable : ' + step, done: false};
        } else if (step === 2) {
          return { value: 'Iterable : ' + step, done: false};
        } else if (step === 3) {
          return { value: 'Iterable : ' + step, done: false};
        }
        return { value: '', done: true };
      }
    }
  },
}

for (const val of iterableObj) {
 console.log(val);
}

실행 결과

 

다음은 제너레이터를 사용한 제너레이터 함수입니다.

function * iterableObj(step) {
  yield 'Iterable : ' + step++;
  yield 'Iterable : ' + step++;
  yield 'Iterable : ' + step++;
}

for (const val of iterableObj(1)) {
 console.log(val);
}

실행 결과

두 개의 예제를 비교해보면, 제너레이터 함수를 사용하는 방식이 코드가 더 간결하고 간단하다는 것을 알 수 있습니다.

 

제너레이터를 사용하여 반복자 구현의 이점

  • Symbol.iterator을 작성할 필요가 없습니다.
  • next() 함수를 구현할 필요가 없습니다.
  • 조건문을 작성할 필요가 없습니다. 일반 함수는 변수 step마다 조건문이 존재하지만, 제너레이터 함수는 조건문이 필요 없습니다.

비동기 함수 구현

제너레이터는 Promise 작업을 단순화할 수 있습니다.

 

다음 예제는 PromiseCallback을 사용하는 코드입니다.

function fetchJson (url) {
  return fetch(url) 
  .then(request => request.text())
  .then(text => { 
    return JSON.parse(text); 
  }) 
  .catch(error => { 
    console.log (`에러 발생: ${error.stack}`); 
  }); 
}

위 예제는 제너레이터와 co.js 라이브러리를 사용하여 다음과 같이 동일하게 구현할 수 있습니다.

const fetchAPI = co.wrap(function* (url) {
  try {
      let request = yield fetch(url);
      let text = yield request.text();
      return JSON.parse(text);
  }
  catch (error) {
      console.log('에러 발생 : ${error.stack}');
  }
});

옵저버로 사용

제너레이터는 next(val) 함수를 사용하여 값을 받을 수도 있습니다. 즉, 제너레이터는 입력을 받을 때까지 모든 행동을 잠깐 중단하며, 새로운 값을 받을 때까지 관찰한다는 의미로 옵저버라고 할 수 있습니다.

 

일반적으로 옵저버는 세 가지 유형을 사용합니다.

  • next() 정상적인 입력을 보냅니다.
  • return() 제너레이터를 종료합니다.
  • throw() 오류를 나타냅니다.

다음은 옵저버 객체입니다.

const observer = {
  next(value) {
    console.log("next -> " + value + "");
  },
  error(err) {
    console.log("에러 발생");
  },
  return() {
    console.log("관측 종료");
  }
}

 

다음은 옵저버 함수입니다.

function observable(observer) { 
  for(var i = 0; i <= 5; i++) { 
    observer.next(i);
  } 
  observer.error();
  observer.return(); 
}

실행 결과

옵저버 함수는 0에서 5까지 숫자를 순차적으로 방출하고 그다음에는 오류를 마지막으로 옵저버를 종료(관측 종료)합니다. 실제로 Observable 구현 방법은 위 예제처럼 간단하지 않으며, 약간 어려운 지식을 요구합니다.

 

옵저버는 위에서 언급했듯이 다음(next), 종료(return) 및 오류(throw)의 세가 지 유형이 존재합니다. 그리고 ES6 반복자(iterator)를 보면 next, throwreturn 세 가지 함수가 존재합니다. 따라서 반복자를 반환하는 제너레이터를 만들 수 있습니다.

 

다음은 반복자를 반환하는 제너레이터 함수입니다.

function * observerGenerator() { 
  try { 
    while(true) { 
      let value = yield 
      console.log('next -> ' + value + ''); 
    }    
  } catch (err) { 
    console.log('에러 발생') ;
  } 
  console.log ('관측 종료');
}

다음은 반복자를 관찰자로 변환하는 함수를 정의합니다.

function createObserver(iterator) { 
  return { 
    next(value) { iterator.next(value) }, 
    error(err) { iterator.throw(err) }, 
    return() { iterator.return() } 
  } 
}

다음은 옵저버 함수를 호출하여 관찰을 시작하는 코드입니다.

observable(createObserver(observerGenerator()));

실행 결과

실행 결과는 이전 예제와 동일합니다.


참조 사이트

https://medium.com/@swazza85/using-es6-generators-as-observers-3c8259d5785

 

Using ES6 Generators As Observers

Prereq: Some familiarity with FRP libraries like RxJS would be helpful.

medium.com

https://javascript.plainenglish.io/how-to-use-the-generator-function-in-javascript-continued-aada07d220c7

 

How to use the Generator function* in JavaScript (continued…)

This is the second part of JavaScript Generator function series. In this article, we will discuss about Generator function Use cases and…

javascript.plainenglish.io

 

반응형

댓글