ES6 Generator 와 ES7 Generator의 차이점?

ES6 Generator

ES6의 Generator 는 Generator body가 .NET 3.5 의 Enumerable과 Java의 Iterator를 섞은 느낌.

function *foo(){
    for(var i=1;i< =10;i++){
        yield i;
    }
}

for (var v of foo()) {
    console.log(v);
}

그러나 일반적인 Iterator와는 달리 ES6의 Generator는 .next() 메소드 호출이 무제한 가능하다.
따라서 .next()를 통해 얻은 값에서 done:true 인 것으로 필터링 해야 한다.
만약 자바처럼 while(b = a.next()) 이런 식으로 반복문을 돌면 무한 반복이 된다.
왜냐면 a.next() 가 다 돌았다고 해도 계속 마지막 값을 전달하는 스펙 때문이다.
수정 : 모두 호출한 다음 next()를 호출하면 다음 yield 값을 불러오는데 없으면 value가 undefined 나온다. 물론 done:true 는 여전하다.

var a = foo(), b;
while(!(b = a.next()).done)
    console.log(b);

이런 측면에서 ES6 Generator는 기존 개발자들에게 Iterator 와는 다른 혼란이 올 수도 있다.
게다가 비동기를 쓰려면 인라인 함수도 Generator 구문을 사용해야 하며,
비동기 연동은 Promise 객체를 사용해야 하고, spawn 함수가 필요하다.
여러가지로 비동기 사용이 번거로워 계륵같은 구문이 아닐 수 없다.

spawn 함수 1
spawn 함수 2

사용 예제

//제목과 챕터별 내용을 외부 경로에서 비동기로 출력하는 예제
//getJSON 함수는 Promise 패턴을 사용한 Ajax 함수라고 가정한다.
function loadStory() {
  return spawn(function *() {
    try {
      let story = yield getJSON('story.json');
      addHtmlToPage(story.heading);
      for (let chapter of story.chapterURLs.map(getJSON)) {
        addHtmlToPage((yield chapter).html));
      }
      addTextToPage("All done");
    } catch (err) {
      addTextToPage("Argh, broken: " + err.message);
    }
    document.querySelector('.spinner').style.display = 'none';
  });
}

ES7 Generator

ES7의 Generator 는 .NET 4.5 의 await와 async 구문을 채용한 듯 한 느낌을 줄 것이다.
사실 사용법도 크게 다르진 않다. 게다가 비동기에 대한 배려가 잘 적용되어 있다.

//제목과 챕터별 내용을 외부 경로에서 비동기로 출력하는 예제
//getJSON 함수는 Promise 패턴을 사용한 Ajax 함수라고 가정한다.
async function loadStory() {
  try {
    let story = await getJSON('story.json');
    addHtmlToPage(story.heading);
    for (let chapter of story.chapterURLs.map(getJSON)) {
      addHtmlToPage((await chapter).html);
    }
    addTextToPage("All done");
  } catch (err) {
    addTextToPage("Argh, broken: " + err.message);
  }
  document.querySelector('.spinner').style.display = 'none';
}

ES6과 달리 spawn 함수가 필요없다. 하지만 비동기인 만큼 Promise 객체를 사용해야 한다는 사실은 변함없다.
하지만 번거로운 spawn 함수가 없는 데다가 코드가 상당히 간결해졌다. 그래서 ES6 스펙이 그냥 ES7로 넘어갔으면 하는 희망사항이 생기기도 한다.

사용법도 간단하다.

(async function() {
  await loadStory();
  console.log("Yey, story successfully loaded!");
}());

단지 실행시에도 Generator 구문을 써야 한다는 점만 빼면 간단하게 실행이 가능하다.

그럼 자바스크립트 개발자의 이해를 돕기 위해 .NET 4.5 에서 추가된 await/async 예제를 보도록 하자.
Async 및 Await를 사용한 비동기 프로그래밍

//msdn.microsoft.com 의 내용 길이를 가져오는 비동기 함수
async Task<int> AccessTheWebAsync()
{ 
    // HTTP 클라이언트 선언
    HttpClient client = new HttpClient();

    // GetStringAsync 함수는 mdsn.microsoft.com 의 HTML 내용을 비동기로 받아둔다.
    // Task 클래스는 JS의 Promise와 동일한 역할을 하며, (자바는 Future<t>) 일단 작업을 받아낸다.
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

    // 일단 작업은 받아냈기 때문에 기다릴 필요 없이 해당 구문으로 넘어간다. 기타 할 것들.
    DoIndependentWork();

    // await를 사용하여 getStringTask 의 작업이 끝날 때까지 AccessTheWebAsync 메소드는 기다린다.
    // 만약 getStringTask 의 작업이 끝나면 urlContents 에 결과값을 전달한다.
    // 이게 끝날 때까지 다음 절차로 넘어갈 수 없다.
    string urlContents = await getStringTask;

    // 이제 위의 비동기 작업이 끝났으니 작업을 마무리할 시간.
    // 비동기 작업을 통해 받아온 urlContents 의 문자열 길이를 출력함으로서 작업을 마친다.
    return urlContents.Length;
}

여기서 공통점이 있다면 Promise 특성과 Generator의 성격을 안다면 알겠지만,
await 구문에 있는 메소드나 Promise 객체가 then 을 통하여 값을 전달할 때 까지는 다음 작업을 수행할 수 없다는 것이다.
ES6의 yield 과는 다르다. yield 구문은 그냥 쌩 값을 반복하듯이 내보내기 때문에 직접 핸들링 해야 한다는 차이점이 있다.
이 차이점을 이해했다면 ES7의 Generator 가 더 진보되고 편해졌다고 느껴질 것이다.

미안하다. 나도 다쓰고 이해시키는 것에 실패했다. 조만간 부가설명이나 글 수정을 통해 개선하겠다.
게다가 일도 바빠서 여기까지.

참고자료

The Basics Of ES6 Generators
ES7 async functions

composite / 2015년 4월 6일 / Piss Development
태그:, , , , , ,

답글 남기기

Your email address will not be published / Required fields are marked *