TIL 13 - 비동기 프로그램 (callback, promise, async&await)

hojung choi·2021년 6월 14일
0

js

목록 보기
13/17
post-thumbnail

사실 나는 지금껏 콜백 함수를 많이 썻다!
전 시간에 배웠던 map, reduce와 같은 콜백함수!
그때는 그냥 어렴풋 매개변수 안에 들어가는 함수 라고 생각하고 넘어갔는데,
이번에는 제대로 정리를 해보려고 한다

비동기 프로그램

짜장면을 시켰다고 가정해보자!

짜장면 배달원이 짜장면을 가지고 왔다.
배달원이 동기적으로 일을 하게되면, 짜장면 배달원은 내가 짜장면을 다 먹을때까지 기다려 그릇을 챙겨 다시 가게로 갔다가 다음 주문인 장소로 배달을 갈 것이다.

매우 비효율적인 방식이다!

자바스크립트는 싱글 스레드 언어이다.
한 번에 한 가지 작업 밖에 수행하지 못한다는 뜻이다.
마치 위의 배달원처럼..

오늘날 웹페이지는 저어어어엉말 많은 데이터와 정보들이 흘러넘친다
이런 곳에서 위의 배달원처럼 코드가 읽혀진다면?
로딩이 다 될때까지 아무런 작업도 할 수 없게된다면?
그 웹페이지는 사용자들의 외면을 받게 될 것이다.

이러한 문제점을 해결하기 위한 해답이 바로 비동기 프로그래밍이다

function increaseAndPrint(n, callback) {
  setTimeout(() => {
    const increased = n + 1;
    console.log(increased);
    if (callback) {
      callback(increased); //함수 호출
    }
  }, 5000);
}

increaseAndPrint(0);
console.log("A");
console.log("C");

A
C
//5초후 1

위의 코드는 setTimeout 비동기적으로 실행되었기 때문에 1 , A , C 가 아닌 A,C,1이 출력된다

그런데,
지금이야 setTimeout 과 같이 비동기가 필요한 부분이 하나였기에 잘 알아볼 수 있지, 이게 3,4개가 중첩되어있다면 어떻게 될까?

👉🏻 callback

function increaseAndPrint(n, callback) {
  setTimeout(() => {
    const increased = n + 1;
    console.log(increased);
    if (callback) {
      callback(increased); //함수 호출
    }
  }, 5000);
}

increaseAndPrint(0, (n) => {
  increaseAndPrint(n, (n) => {
    increaseAndPrint(n, (n) => {
      increaseAndPrint(n, (n) => {
        increaseAndPrint(n, (n) => {
          console.log("작업 끝!");
        });
      });
    });
  });
});

이와 같이 코드가 산을 그리면서 산으로 가는 것을 콜백지옥(callback hell)이라고 한다.
callback hell에서도 잘 돌아가긴 한다. 돌아가긴 하는데...
만약에 저기 안에서 에러가 난 부분을 찾고 고쳐야한다면? 아주 읽기 싫고, 가독성이 떨어지게 된다.

그래서 나오게 된....

👉🏻 Promise

// 기본적으로 PROMISE는 인터페이스와 같으므로 new 생성자를 이용합니다
// Promise는 resolve와 reject에 해당하는 callback 2개를 받는
// 함수를 인자로 받습니다.
let promise = new Promise(function(resolve, reject) {});


//기본예제
let myFirstPromise = new Promise((resolve, reject) => {  
  setTimeout(function(){
    resolve("Success!"); // Yay! Everything went well!
    // reject (new Error('no work'));
  }, 250);
});

//.then 체이닝 예제
myFirstPromise.then((successMessage) => {
  console.log("Yay! " + successMessage);
});

Promise는 함수 안의 작업이 성공적으로 동작했을 때 동작하는 'resolve' callback과 함수가 동작이 실패해서 오류상황이 난 때에 동작하는 'reject' callback을 인자로 받는 함수를 실행한다.

promise를 공부할 때 가장 중요한 포인트는 state로,
pending => fulfilled or rejected

값이 정상적으로 수행이 잘 된다면, resolve로 전달된 값을 받아온다 (then)

function increaseAndPrint(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const increased = n + 1;
      if (increased === 5) {
        const error = new Error();
        error.name = "Error";
        reject(error);
        return;
      }
      console.log(increased);
      resolve(increased);
    }, 1000);
  });
}

increaseAndPrint(0)
  .then(increaseAndPrint)
  .then(increaseAndPrint)
  .then(increaseAndPrint)
  .then(increaseAndPrint)
  .then(increaseAndPrint)
  .catch((e) => {
    console.error(e);
  });

코드가 보기 쉽게 훨 간단해졌다!
비동기 작업의 개수가 많아져도 코드에 깊이가 깊어지지 않는다

그러나, Promise도 단점이 있다

  • error를 잡을 때 어느 지점에서 에러가 났는지 확인이 어렵다
  • 중간에 다른 값이 필요할 때에도 callback과 마찬가지로 코드를 읽기 어렵도록 가독성을 해친다

그래서 생겨난 것이
비동기적인 것을 동기적인 것 마냥...

👉🏻 asyc&await

async와 await는 자바스크립트의 비동기 처리 패턴 중 가장 최근에 나온 문법으로 기존의 비동기 처리 방식인 콜백 함수와 프로미스의 단점을 보완하고 개발자가 읽기 좋은 코드를 작성할 수 있게 도와준다

async function 함수명() {
await 비동기처리메서드_명();
}

먼저 함수의 앞에 async 라는 예약어를 붙인다. 그러고 나서 함수의 내부 로직 중 HTTP 통신을 하는 비동기 처리 코드 앞에 await를 붙인다.
여기서 주의해야 할 점은 비동기 처리 메서드가 꼭 프로미스 객체를 반환해야 await가 의도한 대로 동작한다.
일반적으로 await의 대상이 되는 비동기 처리 코드는 Axios 등 프로미스를 반환하는 API 호출 함수이다

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function process() {
  console.log("안녕하세요");
  await sleep(1000);
  console.log("반갑습니다");
}

process();

📌 TMI

엄청 엄청 엄청 이해하는데에 오래 걸렸던 비동기 처리 방식 ㅠㅠ
일단은 완벽하게 이해하는 것 보다는 (아직은 완벽하게 이해하기에는 무리라고 생각...) 실전 코드를 작성했을 때 아! 맞다 이거는 비동기 처리방식으로 처리해야해! 까지 떠오르면 성공인거다..

profile
🧚🏻‍♀️ Front-End Developer

0개의 댓글