[JS] Callback 함수 => Promise => async-await

badassong·2022년 12월 13일
0

JS

목록 보기
18/31
post-thumbnail

Callback

함수를 실행할 때 함수에 인자를 넣어준다. 함수의 인자에는 함수도 넘길 수 있는데,
그 함수를 Callback 함수라고 한다.

그렇다면 callback함수를 왜 사용하는 것일까?
특정한 API 요청이 끝난 뒤, 그 결과 값을 가지고 다른 요청을 실행시켜야 하는 상황을 가정해보자.
그럴 때 이런 식으로 callback 함수를 사용해서 요청을 실행할 수 있다.

function aaa(qqq){
	// 외부 API에 데이터 요청하는 로직
	// ...
	// ...
	// 요청 끝!
	const result = "요청으로 받아온 데이터 결과값"
	qqq(result) // 요청 끝나면 qqq 실행시키기
}

aaa(result) => {
	console.log("요청이 끝났습니다.")
	console.log("요청으로 받아온 데이터는" + result + "입니다")
}

async/await나 promise 문법이 아직 존재하지 않았던 시기에는
callback 함수를 이용해 데이터를 요청하고 처리했다. 우리는 이미 async/await를 익숙하게 사용하고 있지만, 자바스크립트 비동기 처리의 발전 과정을 이해하고 사용하는 것 또한 중요하다.
다양한 자바스크립트 비동기 처리 방법을 한 번 살펴보자.

콜백 지옥

이렇게 요청이 반복될수록 대각선으로 쑥 파이는 형태가 된다.
지금은 총 3회의 요청만 들어갔지만, API 요청이 2~3번 정도만 더 중첩되어도 코드의 가독성이 심각하게 떨어지게 된다.
이런 현상을 콜백 지옥 (Callback Hell) 이라고 부른다!!

Promise

콜백 지옥과 같은 현상을 막고자 나온 것이 바로 Promise 이다.
promise란 자바스크립트의 비동기 처리, 그 중에서도 특히 외부에서 많은 양의 데이터를 불러오는 작업에 사용되는 객체이다. 이런 promise 객체를 이용해서 만든 라이브러리가 axios이다.
axios.get() 위에 마우스를 올려보면
요청에 대한 반환 값이 Promise로 들어오는 것을 확인할 수 있다.

axios 뿐만 아니라 데이터 통신에 사용되는 현대 라이브러리들은 대부분 Promise를 기반으로 만들어져 있다. 또한 Promise 객체에는 async/await를 붙이는 것이 가능하다.

axios를 사용하지 않고 Promise 객체를 직접 사용하고자 할 때에는 다음과 같이 할 수 있다.

const result = await new Promise((resolve, reject) => {
	const aaa = new XMLHttpRequest();
  aaa.open("get", "http://numbersapi.com/random?min=1&max=200");
  aaa.send();
  aaa.addEventListener("load", (res: any) => {
		resolve(res)
  });
})

// resolve : 요청이 성공했을 경우
// reject : 요청이 실패했을 경우

그렇다면 async/await가 아직 만들어지기 전에는 어떻게 Promise를 사용했을까?
바로 promise 객체에 제공되는 .then이라는 기능을 사용했다.

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);
    });
};

callback에 비해 코드가 간단해진 것을 확인할 수 있다. 또한 콜백 지옥과 같은 현상도 일어나지 않는다.
promise를 사용할 경우 각 요청들이 체인처럼 연결되는데, 이러한 것을 프로미스 체인(Promise chain) 또는 프로미스 체이닝(Promise chaining)이라고 부른다.
그런데 Promise에도 문제가 있다.
콜백 지옥은 해결했지만, 직관적이지 못하다.

async-await

async / await를 이용하면 코드가 직관적이고 심플해진다.

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번입니다~");
};

await를 붙일 수 있는 곳은 Promise 앞이다!!
아무데나 붙일 수 있는 것이 아님!

axios, fetch 등 => 대표적인 Promise(.then, .catch 등의 기능)를 지원하는 기능
axios나 fetch 등을 기다리는 2가지 방법
1. .then() 활용
2. await 활용 => 주의) await는 아무데나 붙인다고 뒤의 것을 기다리는 게 아님!!

resolve => 성공했을때 함수
reject => 실패했을때 함수

매크로-태스크큐(MacroTaskQueue)

우선순위가 낮다!
ex) setTimeout(), setInterval()..

마이크로-태스크큐(MicroTaskQueue)

우선순위가 높다! ex) Promise..

함수가 await를 만나면 그 함수는 마이크로큐안에 들어간다!!

profile
프론트엔드 대장이 되어보쟈

0개의 댓글