자바스크립트에서 비동기적 처리를 위해 쓰는 오브젝트이다. 기본적으로 콜백함수와 구조는 같지만 간결화된 코드로 훨씬 깔끔하게 콜백함수의 처리를 할 수 있다.
Promise의 상태는 총 3가지가 있다.
아래와 같이 Promise가 처음 생성되면 pending(대기) 상태가 된다. promise가 실행 되었지만 브라우저에서 결과를 처리하지 않은 상태다.
const promise = new Promise((resolve, reject) => {});
console.log(promise);
// Promise { <pending> }
아래와 같이 Promise에서 resolve를 실행하면 Fulfilled(이행) 상태가 된다. 조건에 따라 resolve가 나중에 실행되면서 비동기적인 처리가 가능하다.
const promise = new Promise((resolve, reject) => {
resolve();
});
console.log(promise);
// Promise {<fulfilled>: undefined}
Promise에서 reject를 실행하면 Rejected(실패) 상태가 된다. 조건에 따라 reject가 나중에 실행되면서 비동기적인 처리가 가능하다.
const promise = new Promise((resolve, reject) => {
reject();
});
console.log(promise);
//Promise {<rejected>: undefined}
promise의 형태는 1. Producer (promise를 선언하는 부분)과 2. Consumer (promise를 실행하는 부분)으로 나뉜다. 다음은 Producer 부분이다.
const isReady = true;
// 1. Producer
const promise = new Promise((resolve, reject) => {
console.log("Promise is created!");
if (isReady) {
resolve("It's ready");
} else {
reject("Not ready");
}
});
promise는 promise가 만들어지는 순간 즉각적으로 실행되는 함수를 인자로 받는데 이를 executor(실행자, 실행 함수)라고 부른다. executor는 자바스크립트에서 자체 제공하는 콜백함수인 resolve와 reject를 인자로 받는다. 위의 코드에서 'console.log("Promise is created!")' 부분은 즉각적으로 실행이 되고 resolve와 reject는 isReady의 값에 따라서 하나가 실행되게 된다.
// 2. Consumer
promise
.then(message => {
console.log(message);
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log("Done");
});
// Promise is created!
// It's ready
// Done
isReady가 true라면 message에 resolve의 인자가 전달되어 'console.log(message)'가 실행된다. isReady가 false라면 error에 reject에 넣어준 인자가 전달되어 'console.error(error)'가 실행된다. finally()는 무조건 실행되어지는 함수이다.
promise를 활용하여 지난 포스트에서 작성했던 콜백지옥 코드를 깔끔하게 고쳐보겠다.
class UserStorage {
loginUser(id, password) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (
(id === 'ellie' && password === 'dream') ||
(id === 'coder' && password === 'academy')
) {
resolve(id);
} else {
reject(new Error('not found'));
}
}, 2000);
});
}
getRoles(user) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (user === 'ellie') {
resolve({ name: 'ellie', role: 'admin' });
} else {
reject(new Error('no access'));
}
}, 1000);
});
}
}
const userStorage = new UserStorage();
const id = prompt('enter your id');
const password = prompt('enter your passrod');
userStorage.loginUser(id, password)
.then(userStorage.getRoles) // .then(user => userStorage.getRoles()) 형태를 인자가 똑같기 때문에 간단한게 생략할 수 있다
.then(user => alert(`Hello ${user.name}, you have a ${user.role} role`))
.catch(console.log)
기존의 콜백 지옥일 때와 다른 점은 오히려 함수를 구현해주는 부분에서는 프로미스 코드를 넣어주면서 코드량이 조금 늘었지만, 함수를 호출하는 부분에서는 코드 줄 수가 압도적으로 줄어들었다. 이전 콜백 지옥일 때는 인자로 받는 변수와 함수들마다 줄을 구분해주고 콜백 함수를 하나 거쳐갈 때마다 반환값을 다음 콜백 함수 인자로 일일이 명시해줬지만, 프로미스를 쓰면 .then에서 훨씬 간소화된 형태로 함수를 한 줄로 입력받는다. 또한 콜백 지옥에서는 에러가 나는 경우 처리하는 코드를 밑에 다 적어주어야 했지만, 프로미스를 사용하면 .catch 코드만 한 줄 작성하면 .then에서 실행하는 함수가 오류가 나게 되면 모두 밑에 적은 catch에서 처리한다.