[자바스크립트 완벽가이드] - 비동기 자바스크립트

Lee Jeong Min·2022년 7월 5일
5

자바스크립트

목록 보기
14/17
post-thumbnail

자바스크립트 완벽가이드 13장에 해당하는 부분이고, 읽으면서 자바스크립트에 대해 새롭게 알게된 부분만 정리한 내용입니다.

웹 브라우저의 자바스크립트 프로그램은 일반적으로 이벤트 주도적이다.
→ 즉, 프로그램이 실제로 무언가를 실행하기 전에 사용자가 뭔가 클릭하거나 탭하기를 기다린다는 의미

이 장에서는

  1. 콜백
  2. 프라미스
  3. async/await
  4. 비동기 순회

순서로 보면서, 웹 브라우저와 노드의 비동기적 기능에 대해 알아보자.

콜백과 비동기 프로그래밍

가장 기본적인 비동기 프로그래밍: 콜백

콜백함수 예제

  • 타이머(콜백함수)
  • 이벤트(이벤트 핸들러, 이벤트 리스너)
  • 네트워크 이벤트(XMLHTTPRequest와 콜백함수 사용)
  • 노드의 콜백과 이벤트(fs.readFile과 같은 파일 시스템 함수들)

어떤 조건 혹은 이벤트가 일어나면 콜백함수를 호출!

프라미스

프라미스는 콜백 헬 대신 선형에 가까운 프라미스 체인으로 바꾸어 준다.

여기서 설명한 내용 대부분은 모던 자바스크립트 딥 다이브에 나온 내용이며, 다시한번 짚고 넘어가야할 부분은 프라미스 자체가 비동기 작업을 관리하도록 설계되었으며, 작업 2가 비동기라면 콜백이 반환되는 시점에 완료되지 않았을 수도 있다라는 것임을 인지하는것이 중요하다고 생각한다.

그외 키워드

  • Promise.all
  • Promise.allSettled
  • Promise.race()
  • 프라미스 시퀀스

async와 await

async와 await는 프라미스 사용을 극적으로 단순화하며 프라미스 기반의 비동기 코드를 동기적 코드처럼 작성할 수 있게 한다.

await 표현식

await p는 p가 완료될 때까지 대기한다. p가 이행되면 await p의 값은 p가 이행된 값이다.

여러개의 프라미스 대기

let value1 = await getJSON(url1);
let value2 = await getJSON(url2);

// 이를 동시에 가져오려면 아래와 같이 사용가능
let [value1, value2] = await Promise.all([getJSON(url1), getJSON(url2)]);

비동기 순회

for/await 루프

await 표현식과 마찬가지로 for/await 루프 역시 프라미스 기반이다.

// 이를
for(const promise of promises) {
  response = await promise;
  handle(reponse);
}

// 이렇게 바꿀 수 있다.
for await (const reponse of promises) {
  handle(response);
}

두 예제는 똑같이 동작한다.

비동기 이터레이터

비동기 이터레이터는 일반적인 이터레이터와 비슷하지만 중요한 차이가 2개 있다.

  • 비동기 이터러블 객체에서는 심벌 이름 Symbol.asyncIterator를 가진 메서드가 있다.
  • 비동기 이터레이터의 next() 메서드는 직접적으로 순회 결과 객체를 반환하는 것이 아닌, 순회 결과 객체로 해석되는 프라미스를 반환한다.

비동기 제너레이터

비동기 제너레이터는 비동기 함수의 특징과 제너레이터의 특징을 모두 가져서 일반적인 비동기 함수와 마찬가지로 그 안에서 await를 사용할 수 있고, 일반적인 제너레이터와 마찬가지로 yield를 사용할 수 있다.

// await를 사용할 수 있도록 setTimeout()을 감싸는 프라미스 기반 래퍼 함수
function elapsedTime(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function* clock(interval, max=Infinity) {
  for(let count = 1; count <= max; count++) { // 일반적인 for 루프
    await elapsedTime(interval);              // 지정된 시간만큼 대기하고
    yield count;                              // 카운터를 전달한다.
  }
}

// 비동기 제너레이터와 for/await를 사용하는 테스트 함수
async function test() {
  for await (let tick of clock(300, 100)) {
    console.log(tick);
  }
}

비동기 이터레이터 구현

비동기 제너레이터를 사용하지 않아도 다음과 같이 비동기 이터레이터를 직접 만들 수 있다.

객체의 Symbol.asyncIterator() 메서드가 반환하는 객체의 next() 메서드가 순회 결과 객체로 해석되는 프라미스를 반환하도록 만들면 됨

function clock(interval, max = Infinity) {
  function until(time) {
    return new Promise((resolve) => setTimeout(resolve, time - Date.now()));
  }

  // 비동기 이터러블 객체 반환
  return {
    startTime: Date.now(),
    count: 1,
    async next() {
      if (this.count > max) {
        return { done: true };
      }

      let targetTime = this.startTime + this.count + interval;
      await until(targetTime);
      return { value: this.count++ };
    },
    [Symbol.asyncIterator]() {
      return this;
    },
  };
}

이런 비동기 이터레이터의 장점은 비동기 이벤트나 데이터 스트림을 나타낼 수 있다는 점

profile
It is possible for ordinary people to choose to be extraordinary.

2개의 댓글

comment-user-thumbnail
2022년 7월 7일

좋은 글 잘 읽었습니다.
정리 해주셔서 감사합니다

1개의 답글