[데브코스/TIL] DAY24 - JavaScript(11) async await

Minha Ahn·2024년 11월 6일
1

데브코스

목록 보기
21/29
post-thumbnail

🪈 태스크 큐 & 마이크로 태스크 큐

1. 태스크 큐

  • 웹 브라우저의 자바스크립트 엔진이 관리하는 큐
  • Web API 중 비동기 함수의 콜백 함수가 대기하는 공간
  • 콜스택이 비어야 태스크 큐에 있는 작업을 콜스택으로 이동 가능

2. 마이크로 태스크 큐

  • 웹 브라우저의 자바스크립트 엔진이 관리하는 큐
  • Promise의 fulfilled 혹은 rejected 처리 함수가 대기하는 공간
  • 콜스택이 비어야 태스크 큐에 있는 작업을 콜스택으로 이동 가능
  • 태스크 큐보다 먼저 처리



🫸 async, await

  • Promise의 코드를 더 간단하게 만들어주는 문법 (sugar synctax)

1. async

  • async가 붙는 함수의 반환값은 무조건 Promise 객체
  • async가 붙는 함수는 명시적으로 리턴값을 작성하지 않으면 자동으로 undefined 반환
    • 명시적으로 값을 반환하지 않으면 Promise.resolve(undefined) 처럼 동작

2. await

  • await는 함수 호출의 비동기 처리 결과를 기다려주는 역할
  • 함수 내부에서 resolve나 어떤 값이 리턴될 때까지 대기
  • await는 함수 내부에서 유의미한 결과값을 받아야 함.
    • 아무것도 리턴하지 않는다면 undefined를 리턴받은 것처럼 인식

3. 코드 비교-1

// Promise를 사용한 코드
const getSunIcon = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("☀️");
    }, 3000);
  });
};

const getWeatherIcon = () => {
   const sun = getSunIcon();
   //console.log(sun); // 이러면 결과 안나옴!
   sun.then(console.log);
};
// async await를 사용한 코드
const getSunIcon = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("☀️");
    }, 3000);
  });
};

// 동기처럼 보임
const getWeatherIcon = async () => {
   const sun = await getSunIcon();
   console.log(sun);
};

getWeatherIcon()

동작 과정


4. 코드 비교 - 2

const delay = (ms) =>
  new Promise((resolve) => {
    setTimeout(resolve, ms);
  });

const getSunIcon = async () => {
  await delay(1000);
  return "☀️";
};

const getWaveIcon = async () => {
  await delay(1000);
  return "🌊";
};

const getCloudIcon = async () => {
  await delay(1000);
  return "☁️";
};
// 콜백 지옥
const getWeatherIcon = () => {
  getSunIcon() //
    .then((sun) => {
      return getWaveIcon().then((wave) => {
        return getCloudIcon().then((cloud) => {
          console.log(sun, wave, cloud);
        });
      });
    });
};
const getWeatherIcon2 = async () => {
  const sun = await getSunIcon();
  const wave = await getWaveIcon();
  const cloud = await getCloudIcon();
  console.log(sun, wave, cloud);
};

위의 코드로 동기처럼 보여 가독성이 좋아졌다는 점은 있지만 시간이 오래 걸린다는 단점

실행 시간 단축을 위한 패턴

const getWeatherIcon = async () => {
  // 일단 함수 전부 실행 (기다리는 건 나중에)
  const sunPromise = getSunIcon();
  const wavePromise = getWaveIcon();
  const cloudPromise = getCloudIcon();

  const sun = await sunPromise;
  const wave = await wavePromise;
  const cloud = await cloudPromise;
  console.log(sun, wave, cloud);
};
const getWeatherIcon = async () => {
  // 여러 개의 Promise를 동시에 병렬로 처리
  const result = await Promise.all([
    getSunIcon(),
    getWaveIcon(),
    getCloudIcon(),
  ]);
  console.log(result.join(""));
};



😎 REST API

1. REST API

  • HTTP가 가지고 있는 메서드로 API를 잘 만들어 낸 것
  • REST API는 HTTP 메서드를 가지고 있음 => GET, POST, PUT/PATCH, DELETE
  • JsonPlaceholder도 REST API 기반

JsonPlaceholder

  • 샘플 데이터를 제공하는 무료 온라인 REST API 서비스
  • 링크

Thunder Client

  • API 호출을 해볼 수 있는 VSCode 플러그인

REST API를 호출할 수 있는 방법

  • fetch API => Web API
  • axios 라이브러리 => 별도의 설치 필요

2. fetch

  • fetch는 Promise 반환
    • fulfilled이면 최종적으로 Resonponse 객체 반환
    • rejected는 실패에 대한 정보

형식

const getAPI = () => {
  const promise = fetch("https://jsonplaceholder.typicode.com/posts", {
    method: "GET", // GET을 사용할 때는 fetch의 두번째 인자 생략 가능
  });
  // ...
}

fulfilled의 결과 Response 객체

  • 성공적으로 결과를 받아왔을 경우, ok 프로퍼티 값이 true
    • 실패할 경우(정상적이지 않은 리소스 접근 등), ok 프로퍼티 값이 false
    • ok 프로퍼티를 이용해서 에러 헨핸들링 해두기
  • 실제 결과 데이터를 확인하기 위해서는 Response 객체의 json 메서드 필요
    • json 메서드 역시 Promise 반환 => then으로 다룰 필요 있음
const getAPI = () => {
  const promise = fetch("https://jsonplaceholder.typicode.com/posts", {
    method: "GET", // GET을 사용할 때는 fetch의 두번째 인자 생략 가능
  });
  
  promise
    .then((response) => {
      if(!response.ok) throw new Error("fetch error");
      return response.json()
    })
    .then(console.log)
    .catch(() => {
      console.log("error");
    });
}
  • fetch의 resolve와 reject 처리를 함수 내부가 아닌 getAPI() 호출 후에 처리도 가능

추가) 왜 결과 받기를 실패할 때 catch로 잡지 않고 직접 핸들링?

  • 정상적이지 않거나, 무언가 문제가 생길 경우(4xx, 5xx 에러 등)의 에러는 reject가 아닌 resolve로 반환
  • fetch의 성공/실패 여부는 응답 상태 코드에 의존하지 않는다는 것
  • 따라서 응답 상태 코드에서 에러가 발생할 땐 then 내부에서 처리해야 함.

rejected가 되는 경우

  • fetch의 catch는 웹 브라우저에서 반환하는 에러(주로 네트워크 관련 오류)만 가능
  • 콜스택에서 발생하는 오류는 fetch가 처리 불가
  • 이유
    • getAPI 함수 내의 fetch와 관계 없는 코드의 에러는 fetch가 처리하지 않음 (별개의 흐름)
    • fetch는 Promise가 rejected될 경우만 처리하며, 이는 콜스택 에러가 아닌 네트워크 관련 오류

3. 코드

const getApi = async () => {
  // await는 catch가 없어서 try catch로 받아줌
  try {
    const response = await fetch("https:///jsonplaceholder.typicode.com/posts");

    if (!response.ok) throw new Error("Error");

    const data = await response.json(); // json 메서드는 Promise 반환 => await 필요
    console.log(data);
  } catch (e) {
    console.log("r");
    console.error(e);
  } finally {
    console.log("finally");
  }

  // 한번에 쓰는 것도 가능
  // 단 이렇게 하면 response.ok를 체크하지 못함
  const data2 = await (
    await fetch("https:///jsonplaceholder.typicode.com/posts")
  ).json();
  console.log(data2);
};

getApi();



🤓 배운 내용 총정리

function x() {
  console.log("x1");
  y();
  console.log("x2");
}

function y() {
  console.log("y1");
  z();
  console.log("y2");
}

async function z() {
  console.log("z1");
  setTimeout(() => console.log("setTimeout"), 0);
  await w();
  console.log("z2");
}

function w() {
  return new Promise((resolve) => {
    console.log("w1");
    resolve();
    console.log("w2");
  }).then(() => console.log("promiseResolve"));
}

x();

동작 과정





📌 출처

수코딩(https://www.sucoding.kr)

profile
프론트엔드를 공부하고 있는 학생입니다🐌

0개의 댓글