티스토리 뷰

Javascript

es6의 Generator와 es7의 Async/Await

심재철 2020. 5. 14. 10:32

제너레이터 함수는 비동기 작업을 컨트롤 할 수 있을뿐만아니라 동기 작업도 컨트롤 할 수 있게 만든다.

Yield와 Next

제너레이터 함수 내의 yield 키워드가 붙은 함수는 본인의 리턴값을 제너레이터 함수가 호출된 곳으로 전달 할 수 있다. yield키워드는 제너레이터에서의 return 문이라고 생각하면 된다. 다만 함수의 실행이 완전 종료되지 않고 일시정지 된다는 점에서 차이가 있다.

 

yield키워드는 IteratorResult라는 객체를 리턴하는데 그 안에는 valuedone이라는 속성이 들어있다. 제너레이터의 실행이 종료되면 donetrue가 들어갈것이고 value에는 yield키워드가 부착된 함수에서 리턴한 값이 들어가게 된다. 그리고 이 값은 제너레이터를 호출한쪽에서 가져다 사용할 수 있다.

 

제너레이터 함수가 실행되다가 yield 키워드를 만나면 일단 그 키워드가 부착된 함수를 실행시키고 제너레이터 함수는 일시 중단된다. 여기서 한번더 next() 함수를 호출해서 다시 제너레이터 함수의 나머지 부분을 실행할 수 있다.

 

function* UUIDGenerator() {
    let d, r;
    while(true) {
        yield 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            r = (new Date().getTime() + Math.random()*16)%16 | 0;
            d = Math.floor(d/16);
            return (c=='x' ? r : (r&0x3|0x8)).toString(16);
        });
    }
};

이 제너레이터 함수는 현재 시간을 가져와서 uuid를 만들어내는 제너레이터다.

// 제너레이터 함수를 실행하면 제너레이터 객체가 생성된다.
const UUID = UUIDGenerator();

UUID.next() 
// return {value: 'e35834ae-8694-4e16-8352-6d2368b3ccbf', done: false}

UUIDGenerator()를 생성해서 제너레이터 객체를 만들었다. 그리고 next()를 실행시키면 제너레이터 함수 내부가 실행되는데 yield 키워드가 붙은 함수를 실행시키고 그 결과를 제너레이터 호출부로 리턴한다음 멈춘다.

 

좀 더 명확히 이해하기 위해서 크롬 콘솔에 직접 입력해보았다.

제너레이터 객체를 만든다음 next를 실행했다. 위에서 설명한거처럼 yield가 붙은 부분까지 실행한다음 값을 리턴하고 멈춘다.

 

한번더 next를 실행하면 yield 이후 부분이 실행되어 "실행끝"이 출력되었고 다시 whlie 루프를 돌아 "실행됨"이 출력된 후에 yield를 실행하고 결과를 리턴한 다음 멈춘 상태로 대기한다.

 

제너레이터는 이런 방식으로 함수를 중간중간 멈추고 여러번 값을 리턴할 수 있는 함수이다.

 

ES7의 async await

async 키워드는 함수를 비동기 함수라고 표시하기 위해 사용된다. 이 키워드가 붙은 함수는 AsyncFunction이라는 객체를 리턴한다. await키워드는 async 함수 내부에 있는 Promise가 완료될때까지 함수를 멈춰주는 역할을 한다. Promise가 resolve한 값이 await 키워드 호출한 부분으로 리턴된다.

 

쉽게 생각하면 await이라는 키워드는 말 뜻 그대로, Promise가 완료될때까지 기다리는 키워드이다.

중요

  1. await은 async 함수 내부에서만 사용할 수 있는 키워드이다.
  2. async 함수는 항상 promise를 리턴해야한다.
  3. Promise가 정상적으로 실행되는 경우에는 결과값을 리턴하지만 실패하는 경우 에러를 throw한다.
  4. await을 남발하는것은 성능 이슈를 불러일으킨다. 서로 의존적인 비동기 작업만을 await으로 처리해야한다. 예를들어 A라는 비동기 작업이 끝나고 A의 결과값을 이용해서 B라는 비동기 작업을 처리해야 하는 경우는 await으로 처리해야하지만, A와 B가 서로 독립적으로 동작 할 수 있는 경우에는 굳이 await으로 기다릴 필요가 없다는것이다. 이런 경우에는 await을 사용하지말고 그냥 서로 비동기로 동작하게 만들면 된다.
async function asyncFunction() {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("i am resolved!"), 1000)
  });
  
  // Promise가 완료될때까지 함수 실행이 멈춘다.
  const result = await promise; 
  
  console.log(result); // "i am resolved!"
}

asyncFunction();

이 asyncFunction을 실행하면 1초뒤에 프로미스가 resolve될떄까지 await에서 기다린 다음, 콘솔을 찍고 함수가 종료된다.

 

정리

async: 해당 함수가 비동기 작업을 하는 함수라고 표시하기 위해 사용됨.

await: 함수 내부에 있는 비동기 작업을 하는 Promise들이 완료될때까지 기다리기 위해 사용된다.

 

 

Generator 와 Async Await의 비교

  1. async await과 generator 둘다 비동기 함수를 작성하기 위해 사용된다. 
  2. generator는 iterator에 의해 하나의 함수가 yield단위로 나뉘어 실행되는 반면, async await은 await단위로 하나의 함수가 나뉘어 순차적으로 실행된다.
  3. async await은 사용하기 매우 쉽고 제너레이터의 subset이다.
  4. generator는 항상 IteratorResult 객체 {values: X, done: Boolean}를 리턴하는 반면 async 함수는 항상 promise를 리턴한다. (심지어 promise내부에서 에러가 발생한다고 하더라도)
  5. async함수는 제너레이터와 promise로 분해될 수 있다.
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함