Front-End/JavaScript

[JavaScript] Promise 정리 (fetch, async, await, then)

시니철 2024. 9. 21. 17:08

Promise란?

비동기 작업이 완료되면 값을 알려 주는 객체이고, 작업이 완료되면 값을 알려줄 것을 약속하기 때문에 이름이 Promise 입니다.

 

Promise 객체는 세 가지 상태로

`Panding` : 비동기 작업이 끝나기를 기다릴 때

`Fulfilled` : 비동기 작업이 성공적으로 끝났을 때, 비동기 작업의 성공 결과 값으로 갖게 됨.

`Rejected` : 비동기 작업이 실패했을 때, 비동기 작업에서 발생한 오류를 결과 값으로 갖게 됨.

 

Promise 기반의 코드

// Promise 기반
const response = await fetch('...');
const data = await response.json();
console.log(data);

 

평소에 사용하는 코드와 비슷한 모습이며, 비동기 작업을 처리할 때도 우리가 익숙한 형태로 코드를 작성할 수 있다는게 장점이고, 자바스크립트에서 많은 비동기 코드가 Promise를 활용하는 이유입니다.

 

Promise 객체를 다루는 방법은

  1. `.then()` 메소드 + 콜백
  2. `async`와 `await` 문법

위 두 가지로, 처음 배울 땐 `async`와 `await` 문법이 직관적이고 이해하기 쉬울 수 있습니다.


fetch

`fetch`는 웹 리퀘스트를 보낼 때 많이 사용하며, 서버로부터 데이터를 받아올 수 있습니다.

const response = fetch('https://hwiiron.com/api/fetch');

console.log(response); // Promise{ <pending> }

임의로 넣은 URL로 실제로는 동작하지 않습니다.

 

위 코드는 `fetch` 함수가 반환하는 Promise 객체를 `response`에 저장하는 것입니다. `fetch`는 비동기적으로 동작하므로, 데이터를 즉시 반환하지 않고 Promise가 대기(pending) 상태로 나타납니다.

 

데이터를 실제로 사용하려면 `async` / `await`이나 `.then()`을 사용하여 Promise가 완료될 때까지 기다려야 합니다.


await

const response = await fetch('https://hwiiron.com/api/asycnAwait');

console.log(response); // 복잡한 결과 값 출력

 

Promise 객체 앞에 `await` 키워드를 쓰면 Promise의 상태가 `Fulfilled`, `Rejected`가 될 때까지 기다립니다.

  • `Fulfilled`가 되면 Promise 객체의 결과 값을 리턴합니다.
  • `Rejected`가 되면 Promise 객체 결과 값(오류)을 `throw`합니다.

 

`await`, `fetch`를 하여 불러온 결과 값은 복잡하기 때문에 분석된 데이터를 얻기 위해서는 불러온 결과 값(response)을 파싱해야 합니다.

const response = await fetch('https://hwiiron.com/api/asycnAwait');
const data = await response.json()

console.log(data);

 

`.json()` 메소드로 response를 파싱하면 정확한 데이터 값을 출력합니다.

`.json()` 메소드도 오래 걸리는 작업이므로 `await` 키워드를 사용하지 않으면 Promise를 리턴합니다.


async

const response = await fetch('https://hwiiron.com/api/asycnAwait');
const data = await response.json()
console.log(data);

console.log('task 2');

console.log('task 3');

// response를 파싱한 데이터
// task 2
// task 3

async 함수 사용하지 않았을 때

위 소스 코드를 출력하면 `response`를 파싱한 데이터가 먼저 출력되고, 그 뒤에 `task 2`와 `task 3`이 순서대로 출력됩니다.

 

Promise와 `await`으로 비동기 처리를 하려면 `async` 함수를 이용해야 합니다.

async function asycnAwait() {
    const response = await fetch('https://hwiiron.com/api/asycnAwait');
    const data = await response.json()
    console.log(data);
}
// async 화살표 함수 활용법
const asyncAwait = async () => {
    const response = await fetch('https://hwiiron.com/api/asycnAwait');
    const data = await response.json()
    console.log(data);
}

 

async, await 활용

async function asycnAwait() {
    const response = await fetch('https://learn.codeit.kr/api/employees');
    const data = await response.json()
    console.log(data);
}

asycnAwait()

console.log('task 2');

console.log('task 3');

// task 2
// task 3
// response를 파싱한 데이터

 

`async` 함수 안에서 `await`을 사용하면 Promise가 `Fulfilled`가 될 때까지 기다리는 동안, 함수 밖의 코드를 실행하고, Promise가 `Fulfilled` 상태가 되면 다시 함수로 돌아와서 코드를 이어서 실행합니다.

 

async 함수의 리턴값

async function asycnAwait() {
    const response = await fetch('https://learn.codeit.kr/api/employees');
    const data = await response.json()
	return data;
}

const asyncReturn = asycnAwait();

console.log(asyncReturn); // Promise { pending }

 

`async` 함수는 항상 Promise를 리턴합니다.

  • 함수 안에서 Promise를 리턴하면 그 Promise를 그대로 리턴합니다.
  • 함수 안에서 평범한 값을 리턴하면 그 값을 결과 값으로 갖는 Promise를 리턴합니다.
async function asycnAwait() {
    const response = await fetch('https://learn.codeit.kr/api/employees');
    const data = await response.json()
	return data;
}

const asyncReturn = await asycnAwait();

console.log(asyncReturn);

 

따라서 `async` 함수에서 리턴하는 값을 가져오려면 `await`을 활용해야 합니다.

 

동작 순서

  1. async 함수: `asycnAwait` 함수를 실행하면 `pending` 상태의 Promise가 즉시 반환됩니다.
  2. 데이터 반환: 함수 내부에서 `return data;`를 통해 데이터를 반환하면, 해당 데이터가 Promise에 채워지고 Promise의 상태는 `Fulfilled`로 변경됩니다.
  3. await 키워드: `await` 키워드를 사용하여 `asycnAwait` 함수의 실행을 기다리고, 이 결과는 `asyncReturn` 변수에 할당됩니다. 따라서 `async` 함수에서 리턴하는 값을 가져오려면 `await`을 활용해야 합니다.

async와 await 문법에서 try...catch로 오류 처리

`fetch`는 네트워크가 불안정하거나, 서버에서 오류가 나거나, 잘못된 URL에 리퀘스트를 보내는 다양한 이유로 리퀘스트가 실패할 수 있습니다.

 

Promise기반 코드에서 오류를 처리하려면 `try...catch`문을 사용하면 됩니다.

async function asycnAwait() {
    try{
        const response = await fetch('https://learn.codeit.kr/api/employees');
        const data = await response.json()
        console.log(data);
    } catch (error) {
    	console.log('Error');
    }
}

console.log('Finished');

 

코드 흐름

  • `try`블록 안에 코드를 한 줄씩 실행하여 오류가 나지 않으면 마지막 줄까지 실행하고, `try...catch`문 밖으로 나와서 `console.log('Finished');`를 출력합니다.
  • `try`블록 안에서 오류가 나면 오류가 나는 시점에 바로 `catch`문으로 이동해서 `console.log('Error');`를 출력하고 `try...catch`문 밖으로 나와서 `console.log('Finished');`를 출력합니다.

 

`finally`문도 함께 사용할 수 있습니다.

async function asycnAwait() {
    try{
        const response = await fetch('https://learn.codeit.kr/api/employees');
        const data = await response.json()
        console.log(data);
    } catch (error) {
    	console.log('Error');
    } finally {
        console.log('Finished');
    }
}

 

`try`나 `catch`문에서 결과가 어떻든 `finally` 블록 안의 코드 `console.log('Finished');`가 실행됩니다.

 

try...catch문 참고

 

[JavaScript] try...catch, finally (throw)

에러와 에러 객체자바스크립트에서는 코드 실행 중 발생할 수 있는 다양한 에러를 처리하기 위해 에러 객체를 사용합니다. `ReferenceError` : 존재하지 않는 변수나 함수를 호출할 때 발생하는 에

hwiiron.tistory.com


.then() 메소드

자바스크립트에서 비동기 코드를 잘 활용하려면 `async`와 `await`, `.then()` 메소드 둘 다 알고 있는 것이 좋습니다.

 

`async`와 `await` 예시

async function asycnAwait() {
    const response = await fetch('https://hwiiron.com/api/asyncAwait');
    const data = await response.json()
    console.log(data);
}

 

`.then()` 메소드 예시

const dataPromise = fetch('https://hwiiron.com/api/then')
    .then((response) => response.json())
dataPromise.then((data) => console.log(data));

 

동작 순서

  1. `.then()`은 Promise 객체의 메소드로, 앞선 비동기 작업이 완료되면 그 결과 값을 처리하는 콜백을 실행합니다.
  2. 첫 번째 `.then()`에서 `response.json()`을 호출하여 응답 데이터를 `JSON`으로 변환 후 `Promise`를 반환합니다.
  3. 두 번째 `.then()`은 변환된 데이터를 받아서 출력하는 역할을 합니다.

 

`.then()` 메소드 간결하게 사용하는 방법

fetch('https://hwiiron.com/api/then')
    .then((response) => response.json())
    .then((data) => console.log(data));
    
console.log('task 2');

console.log('task 3');

 

Promise 체인의 중요한 점은 비동기 작업이 완료되기 전에 다른 코드들이 먼저 실행된다는 것입니다. 그래서 위 코드는 `task 2`와 `task 3`이 먼저 출력된 후, Promise가 `Fulfilled` 상태가 되면(비동기 작업이 성공적으로 끝나면) 다음 `.then()` 메소드의 콜백이 실행되어 성공한 결과 값을 처리하게 됩니다.

 

또한, `.then()`은 Promise를 반환하기 때문에 뒤에 바로 또 다른 `.then()`을 이어서 붙일 수 있고, 이렇게 여러 `.then()`을 연결하는 방식을 Promise 체인이라고 합니다.


.then() 메소드에서 try...catch로 오류 처리

fetch('https://hwiiron.com/api/then')
    .then((response) => response.json())
    .then((data) => console.log(data))
    .catch((error) => console.log('Error'))
    .finally(() => console.log('Finished));

 

`async`와 `await` 문법에서 `try...catch`로 오류 처리한 방법과 동일하게 `.catch()` 메소드는 Promise 체인에서 오류가 발생하면 콜백을 실행해주고, `.finally()` 메소드는 Promise 체인에서 어떤 일이 발생하든 마지막에 콜백을 실행합니다.


Promise.all() 메소드

`Promise.all()` 메소드는 여러 개의 Promise 객체를 다룰 때 사용합니다.

 

아규먼트로 전달된 Promise들이 모두 `Fulfilled` 상태가 되면 Promise도 `Fulfilled` 상태가 되고, Promise 중 하나라도 `Rejected` 상태가 되면 `Promise.all()`이 리턴하는 Promise는 `Rejected` 상태가 됩니다.

async function asycnAwait() {
    const response = await fetch(`https://hwiiron.com/api/asyncAwait/${id}`);
    const data = await response.json()
    console.log(data);
}

const promises = [];

for (let i = 1; i < 11; i++) {
	promises.push(asycnAwait(i));
}

const result = await Promise.all(promises);
console.log(result);

 

동작 순서

  1. `asyncAwait` 함수는 `fetch`를 통해 데이터를 가져오고 Promise를 반환합니다.
  2. `Promises` 배열은 각 Promise 객체를 담고 있으며, `Promise.all()`은 모든 Promise가 완료될 때까지 기다립니다.
  3. 모든 작업이 완료되면 `result`에 모든 결과가 배열로 반환됩니다.

Promise 종합 정리

 

Codeit/비동기 자바스크립트/promise/Promise 종합 정리.md at main · hwiiron/Codeit

Codeit Sprint. Contribute to hwiiron/Codeit development by creating an account on GitHub.

github.com