[FE] 자바스크립트 비동기 처리(promise, async/await)에 대해

.DS_Store·2023년 5월 3일
0

FrontEnd

목록 보기
8/26
post-thumbnail

Intro

코드를 갖다 쓰기만 해서는 도무지 이해가 안 가는 JavaScript의 비동기 처리를 알아보자! 관성처럼 .then()과 .catch()를 작성하던 과거의 나를 반성한다...
동기, 비동기에 대해서는 잘 정리된 글들이 있으니 참고해보면 좋을 것!

동기 / 비동기가 궁금하다면? 동기와 비동기의 차이

Promise?

Promise는 콜백지옥의 대항마로서(^^;) ES6에서 처음 등장했다.
기본 구조를 먼저 보자.

let myPromise = new Promise((resolve, reject) => {
  ... 
});

myPromise.then(() => {
  ...
}).catch(() => {
  ...
})

new Promise()를 통해 사용할 수 있고, 뒤에 들어오는 화살표 함수에 들어가는 내용이 executor라는, 실행되는 부분이라고 생각하면 된다. 뒤에 들어가는 executor함수는 resolve와 reject라는 인자 중 하나를 호출해 Promise 객체의 상태를 변경한다.

Promise의 세가지 상태

  • 대기(pending): 이행하거나 거부되지 않은 초기 상태

  • 이행(fulfilled): 연산이 성공적으로 완료된 상태

  • 거부(rejected): 연산이 실패한 상태

    Promise 객체는 초기에 대기(pending) 상태이며, 비동기 작업이 완료되면 이행(fulfilled) 또는 거부(rejected) 상태로 바뀐다.

    Promise가 이행 상태가 되었을 때 resolve가 호출되며 그 결과값이 then() 메서드로 전달되고, 거부 상태가 되면 reject가 호출되어 catch() 메서드로 결과값을 전달한다.

사용 예시

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

console.log('start');

delay(1000)
 .then(() => console.log('1 second later'))
 .then(() => delay(2000))
 .then(() => console.log('3 seconds later'))
 .catch((error) => console.error(error))
 .finally(() => console.log('done'));

실행하면 아래와 같은 결과를 얻을 수 있다.


코드를 보면 .then이 쭈우욱 나오는 걸 볼 수 있는데, 이와 같이 then() 과 catch() 문의 체이닝을 통해 로직을 명확히 해나갈 수 있다.
하지만 어딘가 익숙한 이 냄새. 콜백 지옥과 같은 이 냄새.

async/await

그래서 나왔다 async/await!
ES8에서 도입된 개념으로, 프로미스를 기반으로 한 비동기 처리 방식이다. 이를 이용하면 코드가 간결해지고 가독성이 좋아진다고 하는데! 한 번 봐보자.

사용 예시

프로미스를 기반으로 하고 있기 때문에 사용방법이 크게 다르지는 않다.

function fetchUserData(userId) {
 return new Promise((resolve, reject) => {
   setTimeout(() => {
     if (userId === 123) {
       resolve({ id: userId, name: "감자" });
     } else {
       reject(new Error("유저 없다"));
     }
   }, 1000);
 });
}

async function printUserName(userId) {
 try {
   const userData = await fetchUserData(userId);
   console.log(`User name: ${userData.name}`);
 } catch (error) {
   console.error(error.message);
 }
}

printUserName(123); // Output: User name: 감자

printUserName(456); // Output: User not found

async로 감싼 printUserName() 함수에서 try-catch를 사용하여 Promise의 결과를 처리한다. await 키워드는 Promise가 완료될 때까지 함수의 실행을 일시 중지하는 역할을 한다.

그런데 가독성이 좋다..? 잘 와닿지 않는다.
더 쉬운 예시를 보자.

console.log('1');
setTimeout(() => console.log('2'), 1000);
console.log('3');

1이 출력되고 1초 후에 2가 순서는 1, 3, 2로 출력된다. 하지만 위에서 아래의 순서로 읽히지 않는다.

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

const asyncFunction = async () => {
  console.log('1');
  await delay(1000);
  console.log('2');
  console.log('3');
};

asyncFunction();

이렇게 작성하면 1이 출력되고 1초 후에 2가 출력, 이후 3 출력으로 보장되며 위에서 아래로 읽힌다. 이렇게 비동기 함수를 동기적으로 읽을 수 있는 부분에 있어서 가독성이 좋다고 하는듯하다.

정리하면서도 감이 올듯말듯해 명확히 설명하기가 아직 어려운 것 같다. 이번에 최대한 비동기 함수를 정확하게 써보면서 이해도를 높여보겠다!

0개의 댓글