Early Riser
생각정리
Early Riser
전체 방문자
오늘
어제
  • 분류 전체보기 (128)
    • JS (19)
    • React (33)
    • React Native (2)
    • Library, Tool (13)
    • CSS (2)
    • Algorithm (40)
    • Computer Science (3)
    • 회고 (3)
    • AI (13)

블로그 메뉴

  • 홈
  • 태그
  • 방명록
  • 글쓰기

공지사항

인기 글

태그

  • 오늘의불경
  • javascript
  • RNN
  • useState
  • react
  • js
  • 백준
  • 알고리즘
  • BFS
  • boosting
  • 딥러닝
  • 밑바닥
  • 부스트캠프 합격
  • 파이썬
  • 프로그래머스
  • dfs
  • 부스트캠프 합격 후기
  • lightgbm
  • 논문리뷰
  • 비동기
  • 구현
  • 완전탐색
  • local minima
  • global minima
  • 자바스크립트
  • 손실함수
  • 부스트캠프 9기
  • LGBM
  • useEffect
  • 백트래킹

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Early Riser

생각정리

JS

[JS] Promise와 Async/Await의 이해

2024. 8. 23. 15:35

1. 서론

 JavaScript에서 비동기 프로그래밍은 중요한 주제 중 하나이다. 비동기 작업은 코드의 흐름을 방해하지 않고, 동시에 여러 작업을 처리할 수 있게 해준다. 이러한 비동기 작업을 효율적으로 처리하기 위해 Promise와 Async/Await라는 두 가지 주요 도구가 있다. 이 글에서는 Promise가 무엇인지, 그리고 Async/Await와의 차이점이 무엇인지에 대해 좀 더 깊이 있게 다루고자 한다.

2. Promise

JavaScript는 기본적으로 단일 스레드로 동작하기 때문에, 코드가 순차적으로 실행된다. 그러나 웹 애플리케이션에서는 네트워크 요청, 파일 입출력, 타이머 등 시간이 걸리는 작업을 처리해야 하는 경우가 많다. Promise는 비동기 작업이 완료되었을 때 결과를 사용할 수 있게 해주는 객체이다. Promise 따라서 비동기 작업의 결과를 나중에 사용할 수 있게 해준다. 기본적으로 Promise는 다음 세 가지 상태를 가진다:

  1. 대기(Pending): 비동기 작업이 아직 완료되지 않은 상태.
  2. 이행(Fulfilled): 비동기 작업이 성공적으로 완료된 상태.
  3. 거부(Rejected): 비동기 작업이 실패한 상태.

 Promise는 비동기 작업이 완료되면, resolve 또는 reject 함수를 호출하여 작업이 성공했는지 실패했는지를 나타낸다. 성공적으로 완료되면 then 메서드를 통해 결과값을 사용할 수 있고, 실패할 경우 catch 메서드를 통해 에러를 처리할 수 있다.

const promise = new Promise((resolve, reject) => {
  // 비동기 작업 수행
  const success = true;
  if (success) {
    resolve('작업 성공');
  } else {
    reject('작업 실패');
  }
});

promise
  .then((result) => {
    console.log(result); // '작업 성공'
  })
  .catch((error) => {
    console.error(error); // '작업 실패'
  });

 Promise의 가장 큰 장점은 콜백 함수의 중첩으로 인해 발생하는 '콜백 지옥'을 피할 수 있다는 점이다. 복잡한 비동기 작업도 Promise를 사용하면 가독성이 높은 코드로 관리할 수 있다.

2.1 All, AllSetteled: Promise를 iterable하게 사용

 여러 비동기 작업을 처리해야 하는 경우에는 Promise.all이나 Promise.allSettled와 같은 방법을 사용하는 것이 더 간편할 수 있다.

 Promise.all은 여러 개의 Promise를 동시적으로 실행하고, 모든 Promise가 완료될 때까지 기다린다. 하지만 하나라도 실패하면 전체가 실패로 간주된다.

 반면 Promise.allSettled는 모든 Promise의 완료 여부에 상관없이 결과를 반환해 준다. 즉, 하나의 Promise가 실패하더라도 나머지 Promise의 결과를 확인할 수 있다.

 주의해야할 점은 Javascript는 single thread이기 때문에 all/allSettled를 사용한다고 비동기 함수들이 병렬적으로 실행되는 것은 아니란 점이다.(동시적이란 말에 집중하자) 단지 전달한 Promise들을 순회하며 실행하고, 결과값을 배열로 받아볼 수 있는 api일 뿐이다. (For문과 다르게 이전 응답을 기다리지 않고 코드는 순차적으로 실행되니 네트워크 요청은 병렬로 실행되긴 할 것이다. http 버전에 따라 병렬 최대 요청수도 다를것이고)

3. Async/Await: 비동기 작업을 동기처럼 작성

 Async/Await는 ES2017(ES8)에 도입된 비동기 프로그래밍 문법이다. 사실 Async/Await는 Promise를 기반으로 동작한다. 그러나 코드의 구조가 더 간결하고 명확해지기 때문에, 동기적으로 작성된 코드처럼 보이게 하는 것이 큰 장점이다.

Async/Await를 사용하면, 마치 비동기 작업이 동기적으로 처리되는 것처럼 코드를 작성할 수 있다. 아래는 Async/Await를 사용한 예시이다

 3.1 Async: async 함수는 항상 Promise를 return한다.

async function foo() {
  return 42; // return 값이 있는 경우
}

async function bar() {
  console.log('bar 실행'); // return 값이 없는 경우
}

const resultFoo = foo();
const resultBar = bar();

console.log(resultFoo); // Promise { 42 }
console.log(resultBar); // Promise { <fulfilled>: undefined }

resultFoo.then(value => console.log(value); // 42
resultBar.then(value => console.log(value); // undefined

 async 함수는 항상 Promise를 반환한다. 함수 내부에서 return 값을 작성하면, 그 값은 자동으로 Promise.resolve()로 감싸져 반환된다. 예를 들어 return 42라고 하면 실제로는 Promise.resolve(42)가 반환되는 것이다. 반대로 함수 내부에서 return을 생략하면, async 함수는 자동으로 Promise.resolve(undefined)를 반환하게 된다.

 즉, return 여부와 상관없이 async 함수는 항상 Promise를 반환하며, 반환값이 있으면 그 값이 resolve되고 없으면 undefined가 resolve되는 구조를 가진다.

3.2 Await: Promise.then으로 동작

async function fetchData() {
  try {
    const result = await promise;
    console.log('결과:', result);
  } catch (error) {
    console.error('에러 발생:', error);
  }
}

await fetchData();
console.log(1); // fetchData fullfill된 후에 실행(then안의 Callback으로 동작)

fetchData();
console.log(2); // fetchData의 fullfill 여부와 상관없이 실행

 

 위 코드에서 fetchData는 async 함수로 정의되어 있어 항상 Promise를 반환한다. 함수 내부에서는 await를 사용해 promise가 처리될 때까지 기다리고, try…catch를 통해 에러를 처리한다. await fetchData()처럼 호출하면, fetchData가 반환하는 Promise가 fullfill될 때까지 다음 줄(console.log(1))이 실행되지 않고 기다린다. 따라서 console.log(1)은 fetchData가 끝난 후에 출력된다. 반대로 fetchData()를 그냥 호출하면 Promise를 반환하지만 await를 사용하지 않았기 때문에 다음 줄(console.log(2))은 fetchData의 처리 완료와 상관없이 바로 실행된다.

 결국 await은 내부적으로 Promise.then()처럼 동작하며, Promise가 fullfill되면 그 다음 코드가 실행되고, fullfill 여부와 상관없이 실행하고 싶으면 await 없이 그냥 호출하면 된다.

4. 결론

 

 async는 항상 Promise를 반환하는 함수이고, await는 그 Promise가 처리될 때까지 기다리게 해주는 문법적 설탕이다. Promise는 직접 then과 catch로 결과를 처리해야 하기에 가독성이 떨어지고 작성이 불편하지만 async/await는 이를 더 동기처럼 직관적인 코드로 작성할 수 있게 해주는 문법일 뿐, 내부 메커니즘은 Promise와 동일하다.

 

 

 

 

'JS' 카테고리의 다른 글

ES2021에서 추가된 신기능 5가지  (1) 2025.02.13
JavaScript 이벤트 루프 동작방식: Microtask Queue, Macrotask Queue  (0) 2024.11.21
[JS] Javascript에서 동기함수와 비동기함수의 차이  (0) 2024.07.25
[JS] Javascript에서의 FP  (0) 2024.07.24
[JS] Javascript에서의 모듈 시스템  (1) 2024.07.23
    'JS' 카테고리의 다른 글
    • ES2021에서 추가된 신기능 5가지
    • JavaScript 이벤트 루프 동작방식: Microtask Queue, Macrotask Queue
    • [JS] Javascript에서 동기함수와 비동기함수의 차이
    • [JS] Javascript에서의 FP
    Early Riser
    Early Riser
    2년차 프론트엔드 개발자입니다. https://github.com/EarlyRiser42

    티스토리툴바