November 19, 2021

비동기 이터레이터(asynchronous iterator)를 사용하면 비동기적으로 들어오는 데이터를 필요에 따라 처리할 수 있습니다. 네트워크를 통해 데이터가 여러 번에 걸쳐 들어오는 상황을 처리할 수 있게 되죠. 비동기 이터레이터에 더하여 비동기 제너레이터(asynchronous generator)를 사용하면 이런 데이터를 좀 더 편리하게 처리할 수 있습니다.

먼저 간단한 예시를 살펴보며 문법을 익힌 후, 실무에서 벌어질 법한 사례를 가지고 async 이터레이터와 제너레이터가 어떻게 사용되는지 알아보겠습니다.

Untitled

async function* generateSequence(start, end) {
  for (let i = start; i <= end; i++) {
    // await를 사용할 수 있습니다!
    await new Promise(resolve => setTimeout(resolve, 1000));

    yield i;
  }
}

(async () => {
  let generator = generateSequence(1, 5);
  for await (let value of generator) {
    alert(value); // 1, 2, 3, 4, 5
  }
})();

이제 for await...of로 반복이 가능한 async 제너레이터를 사용할 수 있게 되었습니다.

async 제너레이터를 만드는 것은 실제로도 상당히 간단합니다. async 키워드를 붙이기만 하면 제너레이터 안에서 프라미스와 기타 async 함수를 기반으로 동작하는 await를 사용할 수 있습니다.

async 제너레이터의 generator.next() 메서드는 비동기적이 되고, 프라미스를 반환한다는 점은 일반 제너레이터와 async 제너레이터엔 또 다른 차이입니다.

일반 제너레이터에서는 result = generator.next()를 사용해 값을 얻습니다. 반면 async 제너레이터에서는 아래와 같이 await를 붙여줘야 합니다.

실제 사례

지금까진 아주 간단한 예시들만 살펴보며, async 제너레이터에 대한 기초를 다졌습니다. 이제 실무에서 접할법한 유스 케이스를 살펴보겠습니다.

상당히 많은 온라인 서비스가 페이지네이션(pagination)을 구현해 데이터를 전송합니다. 사용자 목록이 필요해서 서버에 요청을 보내면, 서버는 일정 숫자(예를 들어 100명의 사용자) 단위로 사용자를 끊어 정보를 '한 페이지’로 구성한 후, 다음 페이지를 볼 수 있는 URL과 함께 응답합니다.

이런 패턴은 사용자 목록 전송뿐만 아니라, 다양한 서비스에서 찾아볼 수 있습니다. GitHub에서 커밋 이력을 볼 때도 페이지네이션이 사용됩니다.