자바스크립트 완벽 가이드 13장 정리 ( Asynchronous )
포스트
취소

자바스크립트 완벽 가이드 13장 정리 ( Asynchronous )

해당 포스트는 자바스크립트 완벽 가이드라는 교재로 스터디를 하면서 13장을 정리한 포스트입니다.
주관적으로 해석한 내용이 들어가 있어서 잘못된 내용이 포함될 수 있습니다.
또한 교재의 모든 내용을 정리하지 않고 주관적인 판단에 의해 필요한 내용만 작성했습니다.

📦 콜백과 비동기

콜백이란 특정 조건을 만족했을 때 실행할 함수를 의미합니다.
대표적으로 addEventListener()setTimeout()에 전달하는 함수가 있습니다.

0️⃣ 타이머

일정 시간이 지난 뒤 혹은 일정 시간 마다 콜백 함수를 호출할 수 있습니다.

1
2
3
const callback = () => {};

setTimeout(callback, 1000);

1️⃣ 이벤트

미리 특정 행동에 의한 반응을 정해놓고 사용자의 행동을 기다리다가 사용자의 이벤트가 발생하면 콜백함수를 실행합니다.
( 키보드 입력, 마우스 움직임 등 )

이벤트 같은 경우는 사용자의 행동에 의한 약속된 형태의 결과를 인자로 전달해줍니다.

1
2
3
const callback = (e) => {};

document.createElement("button").addEventListener("click", callback);

2️⃣ 네트워크 이벤트

XMLHttpRequest를 이용해서 콜백함수를 등록하고 보낸 요청에 대한 처리로 실행합니다.

1
2
3
4
5
6
7
8
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://jsonplaceholder.typicode.com/users/1");
xhr.send();

// 성공 시 실행할 콜백함수 등록
xhr.onload = () => console.log(xhr.response);
// 실패 시 실행할 콜백함수 등록
xhr.onerror = () => console.error("error");

3️⃣ 노드의 콜백과 이벤트

파일 읽기도 비동기이기 때문에 콜백함수를 통해서 처리합니다.
단, 콜백함수로 처리하는 경우 에러처리 까다로울 수 있어서 첫 번째 인자로 error에 대한 정보를 넣어줍니다.

1
2
3
4
5
6
7
const fs = require("fs");

fs.readFile("tsconfig.json", "utf-8", (error, text) => {
  if (error) console.error(error);

  console.log(text);
});

🕙 Promise

비동기 처리의 결과를 나타내는 객체입니다.
단, Promise로 반복적인 비동기 호출은 불가능합니다. ( setInterval() )

Promise 객체에 콜백함수를 등록하면 비동기 처리가 끝나면 호출됩니다.
또한 Promise에 등록한 콜백함수의 반환 값은 자동으로 동기든 비동기든 Promise 객체가 됩니다.
따라서 Promise 체이닝이라는 기법을 사용할 수 있습니다.

이행, 거부, 대기, 해석 ?

0️⃣ Promise 사용법

then()에 등록된 함수는 한 번만 호출 then()에 전달된 함수는 비동기적으로 호출됨

이름을 동사로 짓는 것이 관습

then()의 두 번째 인자로 예외 처리 가능

1️⃣ Promise 체이닝

Promise.prototype.then()의 반환 값은 항상 Promise 객체이기 때문에 메서드 체이닝이 가능합니다.
Promise.prototype.then()의 반환 값이 Promise가 아니라면 즉시 이행 상태가 됩니다.
( 동기적인 코드가 즉시 이행 상태의 비동기로 바뀝니다. )

1
2
3
4
5
Promise.resolve(1)
  .then((v) => v * 2)
  .then((v) => v * 2)
  .then((v) => v * 2)
  .then(console.log); // 8

2️⃣ Promise의 에러 처리

동기적인 요청에서는 try ~ catch를 통해서 에러나 예외를 처리할 수 있습니다.
하지만 Promise 같은 경우는 비동기적인 요청이기 때문에 예외가 발생하는 위치가 콜스택이 아닙니다.
따라서 정해진 방식대로 예외 처리를 적용해야 합니다.

에러처리에서 중요한 점은 에러가 발생한 순간 에러 처리를 만날 때까지 모두 건너뜁니다.
그리고 에러처리를 만났다고 Promise가 끊기지는 않습니다.
에러처리에서 처리를 한다면 체이닝을 이어갈 수 있습니다.

1. then()의 두 번째 인자 사용

then()의 첫 번째 인자는 성공에 대한 콜백함수를 받고, 두 번째 인자는 실패에 대한 콜백함수를 받습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Promise.resolve(1)
  .then((v) => v * 2)
  .then((v) => v * 2)
  .then((v) => {
    throw new Error("My Error");
  })
  .then(console.log)
  .then(null, (error) => {
    console.log(error); // Error: My Error

    return 200;
  })
  .then((v) => v * 2)
  .then(console.log); // 400

2. catch() 사용

위와 같은 동작을 하지만 Promise.prototype.catch()를 사용하면 조금 더 명시적으로 처리할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Promise.resolve(1)
  .then((v) => v * 2)
  .then((v) => v * 2)
  .then((v) => {
    throw new Error("My Error");
  })
  .then(console.log)
  .catch((error) => {
    console.log(error); // Error: My Error

    return 200;
  })
  .then((v) => v * 2)
  .then(console.log); // 400

3️⃣ Promise 정적 메서드들

Promise.prototype.all(), Promise.prototype.allSettled(), Promise.prototype.any(), Promise.prototype.race(), Promise.prototype.resolve(), Promise.prototype.reject() 등의 메서드에 대한 예시는 1-blue - Promise Static Method를 참고해주세요!

4️⃣ 프로미스 시퀀스

TODO:

🪄 async와 await

0️⃣ async

async로 함수를 선언하면 반드시 반환 값은 Promise 객체가 됩니다.
여기서 중요한 점은 async라는 키워드만 보고 이 함수는 무조건 Promise 객체를 반환한다는 것을 확신할 수 있다는 것입니다.
이 의미는 개발자에게도 통하지만 TypeScript를 사용하는 경우 타입 체커에게도 의미를 전달할 수 있습니다. ( 이펙티브 타입스크립트 Item 25 )
즉, 반환 값의 타입을 확신할 수 있다는 의미입니다.

따라서 하나라도 Promise 객체를 반환하는 함수의 경우에는 async를 붙여주는 것이 좋습니다.

그리고 현재는 async 함수 내부에서 await을 사용할 수 있기 때문에 해당 역할로 많이 사용됩니다.

1️⃣ await

async 함수를 실행하다가 await을 만나면 해당 함수를 멈추고 응답을 기다립니다.

1
2
3
4
5
6
7
const func = async () => {
  // axios 응답 대기
  const { data } = await axios("https://jsonplaceholder.typicode.com/users/1");

  // axios가 응답하면 이후에 실행
  console.log(data); // 요청 데이터 출력
}

🌀 비동기 순회

0️⃣ for/await 루프

결과의 순서는 지키지만 실행은 거의 동시에 수행합니다.

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
const createPromiseTimer = (wait, isSuccess, value) =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      if (isSuccess) {
        resolve("성공 " + value);
      } else {
        reject("실패 " + value);
      }
    }, wait);
  });

const promises = [
  createPromiseTimer(1000, true, "first"),
  createPromiseTimer(2000, true, "second"),
  createPromiseTimer(3000, true, "third"),
];

(async () => {
  for await (const promise of promises) {
    const response = promise;
    console.log(response);
  }
})();

/**
 * 성공 first
 * 성공 second
 * 성공 third
 */

1️⃣ 비동기 제너레이터

TODO:

📮 레퍼런스

  1. « 자바스크립트 완벽 가이드 13장 » ( 데이비드 플래너건 지음, 한성용 옮김, 인사이트, 2022 )
  2. 1-blue - 이터러블
  3. 1-blue - Promise Static Method
  4. 1-blue - 이펙티브 타입스크립트 Item 25
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

러닝 타입스크립트 2장

러닝 타입스크립트 3장