개발 공부 일지/JavaScript

[JS] 자바스크립트 비동기 - Promise

dev-hpk 2024. 9. 21. 11:35

JavaScript에서 비동기 작업을 처리할 때 가장 강력하고 직관적인 도구 중 하나가 Promise입니다. 과거에는 콜백(callback) 방식으로 비동기 처리를 많이 했지만, 콜백 헬(callback hell) 문제로 인해 코드가 복잡해지는 단점이 있었습니다. Promise는 이런 문제를 해결하기 위해 등장한 개념으로, 비동기 작업을 더욱 쉽게 처리할 수 있게 해 줍니다.

 

 

 

Promise - 커버 이미지

 

자바스크립트의 비동기 Promise: 이해와 활용

 

목차

 

1. Promise란?

2. Promise 사용법

3. Promise 체이닝(Chaining)

4. Promise 병렬 처리

5. async와 await

6. 오류 처리(try / catch / finally)

마무리

추천글

 

Promise란?

Promise는 "미래에 완료될" 작업을 나타내는 객체입니다. 현재는 완료되지 않았지만, 언젠가 완료되거나 실패할 작업을 나타내며, 그 결과에 따라 적절한 처리를 할 수 있습니다. Promise는 주로 비동기 작업에서 데이터를 받아오거나, 파일을 읽고 쓰는 등의 상황에서 사용됩니다.

 

Promise의 3가지 상태

  1. Pending(대기 중): 비동기 작업이 아직 완료되지 않은 상태
  2. Fulfilled(이행됨): 비동기 작업이 성공적으로 완료된 상태, 비동기 작업의 성공 결과를  결괏값으로 갖게 됨
  3. Rejected(거부됨): 비동기 작업이 실패한 상태, 비동기 작업에서 발생한 오류를 결과값으로 갖게 됨

 

Promise의 사용 방법

Promisenew Promise()로 생성할 수 있으며, 함수 내부에 resolvereject 콜백을 사용하여 작업의 성공과 실패를 처리합니다.

const myPromise = new Promise((resolve, reject) => {
  let success = true; // 작업의 성공 여부

  if (success) {
    resolve("작업이 성공했습니다!"); // 작업이 성공한 경우
  } else {
    reject("작업이 실패했습니다."); // 작업이 실패한 경우
  }
});

myPromise
  .then(result => {
    console.log(result); // 성공 시 메시지 출력
  })
  .catch(error => {
    console.log(error); // 실패 시 에러 메시지 출력
  });

 

위 코드에서 myPromise비동기 작업을 시뮬레이션합니다. 작업이 성공하면 resolve()가 호출되고, 실패하면 reject()가 호출됩니다. 이후 .then().catch()를 사용해 결과를 처리합니다.

 

Promise 체이닝(Chaining)

Promise의 큰 장점 중 하나는 여러 비동기 작업을 순차적으로 실행할 때 체이닝을 이용할 수 있다는 점입니다. 각 .then() 블록은 이전 작업이 성공적으로 완료된 후 실행되며, 에러 처리는 .catch()에서 처리됩니다.  프라미스 체이닝이 가능한 이유는 promise.then을 호출하면 프라미스가 반환되기 때문입니다. 반환된 프라미스엔 당연히 .then을 호출할 수 있습니다.

fetchData()
  .then(data => {
    return processData(data);
  })
  .then(processedData => {
    return saveData(processedData);
  })
  .then(() => {
    console.log("모든 작업이 성공적으로 완료되었습니다.");
  })
  .catch(error => {
    console.log("에러가 발생했습니다.", error);
  })

 

위 예시에서 fetchData(), processData(), saveData()는 각각 비동기 작업을 처리하며, 각 작업이 성공적으로 완료될 때마다 다음 작업이 순차적으로 실행됩니다.

 

Promise 병렬 처리

비동기 작업을 병렬로 실행해야 할 때는 Promise.all()을 사용할 수 있습니다. 이는 여러 Promise를 동시에 실행하고, 모든 작업이 완료될 때까지 기다린 후 그 결과를 한 번에 반환합니다.

const promise1 = fetchDataFromAPI1();
const promise2 = fetchDataFromAPI2();
const promise3 = fetchDataFromAPI3();

Promise.all([promise1, promise2, promise3])
  .then(results => {
    console.log("모든 데이터를 성공적으로 가져왔습니다.", results);
  })
  .catch(error => {
    console.log("에러가 발생했습니다.", error);
  })

 

Promise.all()은 배열로 전달된 모든 Promise가 완료될 때까지 기다렸다가 결과를 반환합니다. 하나라도 실패하면 전체가 실패한 것으로 간주되어 에러가 발생합니다.

 

async와 await

asyncawaitPromise를 더욱 간결하고 직관적으로 작성할 수 있도록 도와주는 문법입니다. 비동기 작업을 마치 동기 작업처럼 작성할 수 있어 가독성이 좋아집니다. async 함수는 항상 Promise를 리턴하기 때문에 리턴된 Promise 객체의 값을 가져오려면 항상 await을 사용해야 합니다.

// 일반 함수
async function getData() {
  const response = await fetch('fecthUrl');
  const data = await response.json();
  return data;
}

const fetchData = await getData();
// 화살표 함수
const getData = async () => {
  const response = await fetch('fecthUrl');
  const data = await response.json();
  return data;
}

const fetchData = await getData();

 

위 코드에서 await 키워드는 Promise가 완료될 때까지(fulfilled 또는 rejected) 기다린 후 결과를 반환합니다.

  • Fulfilled 상태면 Promise 객체의 결과를 리턴
  • Rejected 상태면 Promise 객체의 오류를 throw

 

오류 처리

try & catch를 이용한 오류 처리

async function getData() {
  try {
    const response = await fetch('fetchUrl');
    const data = await response.json();
    console.log('데이터를 받아오는데 성공했습니다.', data);
  } catch(error) {
    console.log('에러가 발생했습니다.', error);
  }
}

 

위 코드에서 실행되는 코드를 try로 감싸고, catch 안에서 에러를 처리하고 있습니다. try로 감싼 코드에서 에러가 발생되면 catch의 error 변수로 에러가 전달되고 catch문 안에서 에러가 처리됩니다. 만약 에러가 발생하더라도 꼭 실행해야 하는 코드가 있다면 finally를 사용하면 됩니다.

try & catch & finally를 이용한 오류 처리

async function getData() {
  try {
    const response = await fetch('fetchUrl');
    const data = await response.json();
    console.log('데이터를 받아오는데 성공했습니다.', data);
  } catch(error) {
    console.log('에러가 발생했습니다.', error);
    return;
  } finally {
    console.log('try & catch의 결과와 상관없이 실행');
  }
}

 

위 코드를 보면 catch에서 에러 처리 후 return을 해 함수가 종료돼야 하지만, finally가 있기 때문에 종료되지 않고 console.log()의 결과가 출력됩니다.

Promise 체인의 오류 처리

fetch('fetchUrl')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.log('에러가 발생했습니다.', error))
  .finally(() => console.log('try & catch의 결과와 상관없이 실행'));

 

 

 

 

Promise자바스크립트에서 비동기 작업을 더욱 효율적으로 처리하는 방법을 제공합니다. 콜백 함수에 비해 코드가 간결해지고, 체이닝과 병렬 처리를 통해 복잡한 비동기 로직도 쉽게 다룰 수 있습니다. 또한 async/await를 통해 가독성을 한층 더 높일 수 있습니다.

 

 

추천글

2024.09.20 - [개발 공부 일지/JavaScript] - [JS] 비동기 자바스크립트 - 콜백(Callback)

 

[JS] 자바스크립트 비동기 - 콜백(Callback)

JavaScript의 코드가 실행되는 방식은 핵심 개념입니다. JavaScript는 단일 스레드로 작동하지만, 메인 스레드를 막지 않고 비동기 작업을 처리하는 데 강점을 가지고 있습니다. 여기서 콜백과 비동기

dev-hpk.tistory.com