프로미스(Promises)#
프로미스(Promise)는 자바스크립트에서 비동기 처리를 위해 사용되는 객체이다.
프로미스(Promise)는 비동기 작업의 최종 완료 또는 실패를 나타내는 객체이다.
이는 비동기 처리를 동기적으로 처리할 수 있게 해주며, 콜백 함수의 단점을 보완한다.
프로미스(Promise)의 상태#
프로미스(Promise)는 세 가지 상태를 가진다.
- 대기(Pending): 초기 상태, 비동기 처리 로직이 아직 완료되지 않은 상태
- 이행(Fulfilled): 비동기 처리가 성공적으로 완료되어 프로미스(Promise)가 결과 값을 반환한 상태
- 거부(Rejected): 비동기 처리가 실패하거나 오류가 발생한 상태
Promise 생성자와 Executor 함수의 기본 구조#
프로미스(Promise)는 new Promise()
생성자를 통해 생성된다.
Promise를 생성할 때는 다음과 같은 구조를 사용한다:
1
2
3
4
5
6
7
8
| const myPromise = new Promise(
// 여기서부터 executor 함수 시작 ↓
(resolve, reject) => {
// 이 함수가 executor 함수입니다
// 여기서 비동기 작업을 수행합니다
}
// executor 함수 끝 ↑
);
|
이때 Promise 생성자에 전달되는 함수를 “executor” 함수라고 부른다.
이 함수는 resolve와 reject 두 가지 함수를 매개변수로 받는다.
executor 함수는 Promise가 생성될 때 자동으로 실행된다.
이 함수는 비동기 작업을 수행하고, 작업의 결과에 따라 Promise의 상태를 변경하는 역할을 한다.
resolve 함수는 Promise가 성공적으로 완료되었을 때 호출된다.
이 함수는 Promise의 상태를 ‘fulfilled’로 변경하고, 결과값을 저장한다.
reject 함수는 Promise가 실패했을 때 호출
이 함수는 Promise의 상태를 ‘rejected’로 변경하고, 에러 정보를 저장한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // Promise의 상태 변화 예시
function checkOrderStatus(orderNumber) {
return new Promise((resolve, reject) => {
console.log('주문 상태 확인 중…'); // Pending 상태
setTimeout(() => {
const order = findOrder(orderNumber);
if (order) {
resolve(order); // Fulfilled 상태
} else {
reject('주문을 찾을 수 없습니다.'); // Rejected 상태
}
}, 1000);
});
}
|
중요한 특징들#
한 번만 상태 변경 가능
1
2
3
4
5
| const promiseExample = new Promise((resolve, reject) => {
resolve('첫 번째 resolve'); // 이것만 적용됨
resolve('두 번째 resolve'); // 무시됨
reject(new Error('에러')); // 무시됨
});
|
에러 처리
1
2
3
4
5
6
7
8
9
10
| const promiseWithError = new Promise((resolve, reject) => {
try {
// 위험한 작업 수행
const result = riskyOperation();
resolve(result);
} catch (error) {
// 에러 발생 시 자동으로 reject 처리
reject(error);
}
});
|
비동기 작업 래핑
1
2
3
4
5
6
7
8
9
10
11
12
| function readFileAsync(filename) {
return new Promise((resolve, reject) => {
// 기존의 콜백 기반 API를 Promise로 래핑
fs.readFile(filename, 'utf8', (error, data) => {
if (error) {
reject(error);
} else {
resolve(data);
}
});
});
}
|
프로미스(Promise)의 사용#
프로미스(Promise)는 then(), catch(), finally() 메서드를 통해 처리된다.
- then(): 프로미스(Promise)가 이행되었을 때 실행될 콜백 함수를 등록
- catch(): 프로미스(Promise)가 거부되었을 때 실행될 콜백 함수를 등록
- finally(): 프로미스(Promise)의 성공 또는 실패와 상관없이 실행될 콜백 함수를 등록
1
2
3
4
5
6
7
8
9
10
| promise
.then(result => {
console.log(result);
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log('작업 완료');
});
|
프로미스 체이닝 (Promise Chaining)#
프로미스(Promise)는 then() 메서드를 연속적으로 호출하여 여러 비동기 작업을 순차적으로 처리할 수 있다.
이를 프로미스 체이닝이라고 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| // 주문부터 배달까지의 전체 프로세스
function processOrder(orderDetails) {
validateOrder(orderDetails)
.then(validatedOrder => {
// 주문 확인 후 결제 처리
return processPayment(validatedOrder);
})
.then(paymentResult => {
// 결제 완료 후 주방에 전달
return sendToKitchen(paymentResult.orderId);
})
.then(kitchenConfirmation => {
// 음식 준비 완료 후 배달 시작
return startDelivery(kitchenConfirmation.orderId);
})
.then(deliveryInfo => {
console.log('배달이 시작되었습니다:', deliveryInfo);
})
.catch(error => {
// 어느 단계에서든 발생한 에러를 처리
console.error('주문 처리 중 오류 발생:', error);
});
}
|
프로미스(Promise)의 장점#
- 콜백 지옥을 해결하여 코드의 가독성을 향상시킨다.
- 비동기 작업의 순서를 보장하고 에러 처리를 용이하게 한다.
- 여러 비동기 작업을 병렬로 처리할 수 있는 메서드(Promise.all, Promise.race 등)를 제공한다.
병렬로 처리할 수 있는 메서드#
Promise.all() 사용하기#
여러 Promise를 동시에 처리하고 싶을 때 사용한다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // 여러 재료의 재고를 동시에 확인
function checkIngredients(dish) {
const ingredients = dish.requiredIngredients;
const stockChecks = ingredients.map(ingredient =>
checkStock(ingredient)
);
return Promise.all(stockChecks)
.then(results => {
// 모든 재료의 재고 확인이 완료됨
console.log('모든 재료가 준비되었습니다');
return true;
})
.catch(error => {
// 하나라도 재고가 없으면 실패
console.error('재료 부족:', error);
return false;
});
}
|
Promise.race() 활용#
여러 Promise 중 가장 먼저 완료되는 것만 처리하고 싶을 때 사용한다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // 가장 빨리 응답하는 배달 기사 찾기
function findAvailableDriver(deliveryAddress) {
const drivers = ['김기사', '이기사', '박기사'];
const driverPromises = drivers.map(driver =>
checkDriverAvailability(driver, deliveryAddress)
);
return Promise.race(driverPromises)
.then(firstAvailableDriver => {
console.log(`배달 기사가 배정되었습니다: ${firstAvailableDriver}`);
return firstAvailableDriver;
})
.catch(error => {
console.error('사용 가능한 배달 기사를 찾을 수 없습니다.');
throw error;
});
}
|
실제 활용 예시#
API 호출 처리#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| function fetchUserData(userId) {
return fetch(`/api/users/${userId}`)
.then(response => {
if (!response.ok) {
throw new Error('사용자 정보를 가져올 수 없습니다.');
}
return response.json();
})
.then(userData => {
console.log('사용자 데이터:', userData);
return userData;
})
.catch(error => {
console.error('에러:', error);
throw error;
});
}
|
파일 업로드 처리#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| function uploadFile(file) {
return new Promise((resolve, reject) => {
const formData = new FormData();
formData.append('file', file);
fetch('/api/upload', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(result => {
if (result.success) {
resolve(result.fileUrl);
} else {
reject(new Error(result.error));
}
})
.catch(error => reject(error));
});
}
|
타임아웃 처리#
1
2
3
4
5
6
7
8
| function fetchWithTimeout(url, timeout = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('요청 시간 초과')), timeout)
)
]);
}
|
프로미스(Promise)와 async/await#
ES2017에서 도입된 async/await 문법은 프로미스를 기반으로 동작하며, 비동기 코드를 더욱 직관적으로 작성할 수 있게 해준다.
async 함수는 항상 프로미스를 반환하며, await 키워드는 프로미스가 처리될 때까지 함수의 실행을 일시 중지한다.
1
2
3
4
5
6
7
8
| async function asyncFunction() {
try {
const result = await someAsyncOperation();
console.log(result);
} catch (error) {
console.error(error);
}
}
|
참고 및 출처#