비동기로 작동하는 코드를 제어하기 위해 사용하는 것으로, 비동기적으로 수행한 작업이 끝났음을 알려주는 프로미스 객체를 뜻한다. Callback Hell을 방지할 수 있다는 장점이 있다.
pending
: 이제 막 프로미스가 만들어져서 작업이 끝나지 않은 상태fulfilled
: 비동기 처리를 수행하는 작업(executor(콜백 함수)
)가 성공적으로 작동한 상태rejected
: 작업 중 에러가 발생한 상태.then
: executor
에 작성했던 코드들이 정상적으로 처리된 경우 (그 실행 결과를 다루기 위해) 실행되는 메서드.catch
: executor
에 작성했던 코드들에 에러가 발생한 경우 (에러에 접근하기 위해) 실행되는 메서드.finally
: executor에 작성했던 코드의 처리 성공 여부와 관련없이 마지막으로 실행되는 함수여러 가지 프로미스가 체이닝 된 경우에는 맨 아래에서 .catch
를 사용하면 어디에서 에러가 발생했는지 알 수 없다. 따라서 아래와 같이 getSeed 작업 시 발생하는 에러를 잡는 것이 중요한 상황이라면 .catch
위치를 조정하면 된다.
이렇게 할 경우 프로미스 체이닝에 지장이 생기지 않도록 리턴 값을 지정해줄 수 있어서 좋다.
getSeed()
.catch((error) => {
console.log(error.name);
return "basic seed"; // 에러 발생 시 대체할 값을 지정
})
.then(seed => getPlant(seed))
.then(plant => console.log(plant));
TIP. 전달하는 인자와 호출하는 인자가 동일한 경우 아래와 같이 변형할 수 있다.
// 1과 2는 같은 역할을 한다 .then(seed => getPlant(seed)) // 1 .then(getPlant) // 2 .then(plant => console.log(plant)) // 1 .then(console.log) // 2
모든 프로미스를 병렬적으로 한 번에 실행해주는 메서드이다. 실행하고자 하는 프로미스들을 배열 안에 담아 인자로 전달하면 된다.
이를 이용해 복수의 유저 정보 객체를 하나의 배열에 담아 resolve하는 Promise 객체를 반환하는 함수 readAllUsers
를 작성해 보았다.
1. 첫 번째 시도 : 배열 안에서 .then 사용
메서드의 인자로 들어가는 배열 안에서 로직을 해결하는 것이 깔끔하다는 생각이 들어 아래와 같이 작성했다.
const readAllUsers = () => {
return Promise.all(
[
getDataFromFilePromise(user1Path).then((data) => JSON.parse(data)),
getDataFromFilePromise(user2Path).then((data) => JSON.parse(data))
]
);
}
2. 두 번째 시도 : 구조 변경, .map 활용
실시간 세션에서 레퍼런스 코드를 보고 나니 배열 외부, 즉 Promise.all
에 .then
메서드를 연결하여 추가적인 작업(JSON 변환 등)을 하는 것이 훨씬 깔끔하다는 생각이 들었다. 유저 정보가 많은 경우를 대비하여 하드코딩하지 않고 .map
메서드를 사용한 점도 인상적이었다.
세션 다음 날, 레퍼런스 코드의 장점을 살리기 위해 노력하며 스스로 다시 작성해 보았다.
const readAllUsers = () => {
const paths = [user1Path, user2Path];
const dataPromises = paths.map((path) => getDataFromFilePromise(path));
return Promise.all(dataPromises)
.then((dataList) => dataList.map((data) => JSON.parse(data)));
}
Promise.all
과 달리 작업 중 실패한 프로미스가 있는지 여부와 관계 없이 모든 결과를 배열에 담아 반환한다.
// 실행 결과
[
{ status: 'fulfilled', value: 'Lemon cake' },
{ status: 'fulfilled', value: 'Strawberry cake' },
{ status: 'rejected', reason: Error: ... },
]
작업이 정상적으로 처리되었을 때 실행할 코드를 보다 간략하게 작성할 수 있다.
// Promise
function getPlant (seed) {
return new Promise ((resolve, reject) => {
resolve("plant");
}
}
// Promise.resolve
function getPlant (seed) {
return Promise.resolve(seed => "plant");
}