Callback / Promise / async, await

김무연·2023년 12월 5일
0

Frontend

목록 보기
2/12

Callback 함수

다른 함수가 실행을 끝낸 뒤 실행되는 함수, 즉 코드를 통해 명시적으로 호출되는 함수가 아닌, 함수를 등록해놓은 후 어떠한 이벤트가 발생했거나 특정 시점에 도달했을 떄 시스템에서 호출하는 함수를 뜻합니다

사용이유

기본적으로 Javascript는 동기적 프로그래밍 언어이다. 하지만 Callback함수를 이용해 비동기적 프로그래밍을 할 수 있습니다

또한 javascript는 싱글스레드를 사용하는데, 블로킹을 방지해줍니다. 따라서 싱글스레드가 논블로킹으로 동작하게 합니다.

블로킹 : 하나의 요청에 대한 응답이 와야만 다음 작업을 시작하는 방식
논블로킹 : 하나의 작업이 진행되는 동안 시간이 오래 걸리는 작업은 따로 마련한 공간에 던져 실행하는 방식

주의점

  • 콜백지옥은 콜백함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 감당하기 힘들정도로 깊어지는 현상입니다.
  • 주로 이벤트 처리나 서버 통신과 같은 비동기 적인 작업을 수행하기 위해 이런 형태가 자주 등장하는데, 가독성이 떨어지면서 코드를 수정하기 어려워집니다.
  • 비동기적인 작업을 수행하기 위해 콜백함수를 익명함수로 전달하는 과정에서 생기는 콜백지옥을 Promise, async/await 등을 사용해 방지할 수 있습니다.

업로드중..

Promise

Promise 객체는 자바스크립트에서 비동기 처리에서 사용되는 객체입니다. 주로 서버에서 받아온 데이터를 화면에 표시하기 위해서 사용하며 데이터를 받아오기도 전에 데이터를 화면에 표시하려고 하면 오류가 발생하거나 빈 화면이 뜨게 되는데, 이러한 문제를 해결하기 위한 방법 중 하나입니다.
프로미스에는 세 가지 상태가 있습니다. 여기서 상태란 처리 과정을 의미합니다.

  • Pending(대기): 비동기 처리 로직이 아직 완료되지 않은 상태
  • Fulfilled(이행): 비동기 처리가 완료되어 프로미스가 결과 값을 반환해 준 상태
  • Rejected(실패): 비동기 처리가 실패하거나 오류가 발생한 상태

콜백 지옥과 같은 현상을 막고자 나온 것이 바로 Promise입니다. Promise가 콜백을 대체하는것은 아니지만 콜백을 예측가능한 패턴으로 사용할 수 있게 하며 Promise없이 콜백만 사용했을 때 예상치 못한 동작을 막아주거나 힘든 버그를 상당 수 해결해 줄 수 있습니다.

const onClickPromise = () => {
  axios
    .get("http://numbersapi.com/random?min=1&max=200")
    .then((res) => {
      const num = res.data.split(" ")[0];
      return axios.get(`https://koreanjson.com/posts/${num}`);
    })
    .then((res) => {
      const userId = res.data.UserId;
      // prettier-ignore
      return axios.get(`https://koreanjson.com/posts?userId=${userId}`)
    })
    .then((res) => {
      console.log(res.data);
    })
    .catch(
	// reject가 호출되면 catch가 실행
    )
    .finally(
        // 콜백 작업을 마치고 무조건 실행되는 finally(생략가능)
};

위 코드 처럼 callback에 비해 코드가 비교적 간단해 지고, 콜백 지옥과 같은 현상도 일어나지 않습니다.

promise를 사용할 경우 각 요청들이 체인처럼 연결되는데, 이러한 것을 프로미스 체인 또는 프로미스 체이닝이라고 부릅니다.

문제점

  1. 호출에 대한 결과값을 상수에 담아 사용하는 것이 불가능 합니다
const onClickPromise = () => {
  const result = axios
    .get("http://numbersapi.com/random?min=1&max=200")
    .then((res) => {
      const num = res.data.split(" ")[0];
      return axios.get(`https://koreanjson.com/posts/${num}`);
    })
    .then((res) => {
      const userId = res.data.UserId;
      // prettier-ignore
      return axios.get(`https://koreanjson.com/posts?userId=${userId}`)
    })
    .then((res) => {
      console.log(res.data);
    });
	console.log(result)
};

업로드중..

위의 코드를 실행시키면 Promise의 형태로 들어오는 것을 확인할 수 있습니다.

  1. 코드의 순서가 직관적이지 못합니다.
const onClickPromise = () => {
  console.log("여기는 1번입니다~");
  axios
    .get("http://numbersapi.com/random?min=1&max=200")
    .then((res) => {
      console.log("여기는 2번입니다~");
      const num = res.data.split(" ")[0];
      return axios.get(`https://koreanjson.com/posts/${num}`);
    })
    .then((res) => {
      console.log("여기는 3번입니다~");
      const userId = res.data.UserId;
      // prettier-ignore
      return axios.get(`https://koreanjson.com/posts?userId=${userId}`)
    })
    .then((res) => {
      console.log("여기는 4번입니다~");
      console.log(res.data);
    });
  console.log("여기는 5번입니다~");
};

위와 같은 코드에서 console을 보면 1 > 5 > 2 > 3 > 4 순서로 출력됩니다. 이것은 axios를 이용한 비동기 작업이 TaskQueue 안에 들어가, 순서가 뒤로 밀리기 때문입니다.

이러한 문제점들을 async, await를 통해 해결이 가능합니다.

async/await

async & await는 비동기식 코드를 동기식으로 표현하여 간단하게 나타내줍니다. 또한 promise의 직관적이지 못한 점 등 단점을 해결해주었습니다.

일단 코드부터 보자면 아래와 같습니다.

const onClickAsyncAwait = async () => {
  console.log("여기는 1번입니다~");
  // prettier-ignore
  const res1 = await axios.get("http://numbersapi.com/random?min=1&max=200");
  const num = res1.data.split(" ")[0];

  console.log("여기는 2번입니다~");
  const res2 = await axios.get(`https://koreanjson.com/posts/${num}`);
  const userId = res2.data.UserId;

  console.log("여기는 3번입니다~");
  // prettier-ignore
  const res3 = await axios.get(`https://koreanjson.com/posts?userId=${userId}`)
  console.log(res3.data);
  console.log("여기는 4번입니다~");
};

console에 출력되는 것도 1 > 2 > 3 > 4 로 출력이 되며, res1 등 변수에 값을 할당할 수도 있습니다.

async & await 기본 문법

async function 함수명(){
	await 비동기_처리_메소드_이름()
}
  • 함수의 앞에 async라는 예약어를 붙입니다.
  • 함수의 내부로직중 HTTP통신을 하는 비동기 처리 코드 앞에 await을 붙입니다.
    • 비동기 처리 메서드가 꼭 프로미스 객체를 반환해야 await가 의도한 대로 동작한다.
    • 일반적으로 await의 대상이 되는 비동기 처리 코드는 Axios등 프로미스를 반환하는 API호출 함수이다.
  1. async 함수
  • async는 function 앞에 위치한다.
  • function앞에 async를 붙이면 해당 함수는 항상 프로미스를 반환한다.
  • Promise가 아닌 값을 반환하더라도 이행상태의 Promise로 값을 감싸 이행된 프로미스가 반환되도록 한다.
  1. await
  • await은 async함수 안에서만 동작한다.
    Js는 await을 만나면 Promise처리가 될 때까지 기다리고 결과는 그 이후에 반환된다.

예외처리를 해줄 때는 try, catch를 사용하면 됩니다

async function loadData(){
	try{
    	const result = await Data()
        console.log(result)
    } catch(e){
    	console.log(e)
    }
}
profile
Notion에 정리된 공부한 글을 옮겨오는 중입니다... (진행중)

0개의 댓글