Promise란 어떠한 값을 미래에 반환하는 자바스크립트 오브젝트다.
Promise는 비동기 작업이 (성공 또는 실패로) 끝났을 때, 그 다음 수행 내용을 처리할 수 있게 하는 도구다.
그냥 콜백함수를 넘겨주는 방식으로도 할 수 있지만 Callback Hell
문제 때문에 Promise가 필요하다.
Promise의 상태는 총 3가지다
const pr = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('Good');
// reject('Bad');
}, 3000);
});
pr.then(
(result) => console.log('Success!', result),
(error) => console.log('Error!', error)
)
위 처럼 Promise는 생성시 콜백함수 하나를 넘겨준다.
Promise에 넘긴 콜백함수 내용이 메인 로직이다.
then()
메소드의 두 콜백 함수 중 어떤 것이 실행되는지가 결정된다.근데 then()
메소드에 두 콜백함수를 같이 넣어주는 대신에
catch()
메소드를 사용하면 더 가독성 좋게 만들 수 있다.
아래 예제코드는 위 예제코드와 동작이 같다.
const pr = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('Good');
// reject('Bad');
}, 3000);
});
pr.then(
(result) => console.log('Success!', result)
).catch(
(error) => console.log('Error!', error)
)
finally()
메소드도 있는데, 여기에 인자로 넣어주는 콜백함수는pr.then(
(result) => console.log('Success!', result)
).catch(
(error) => console.log('Error!', error)
).finally(
() => console.log('Done.');
)
우선 위 Task들을 정의하고,
각 Task를 순서대로 처리한 후 console.log('Done'); 을 하는 시뮬레이션을 구현해보자.
const task1 = (callback) => {
setTimeout(() => {
console.log('Task 1 is completed!');
callback();
}, 2000);
};
const task2 = (callback) => {
setTimeout(() => {
console.log('Task 2 is completed!');
callback();
}, 500);
};
const task3 = (callback) => {
setTimeout(() => {
console.log('Task 3 is completed!');
callback();
}, 1000);
};
task1(() => {
task2(() => {
task3(() => {
console.log('Done.');
});
});
})
아래 코드는 console.log 가 2, 3, 1, Done 순으로 찍힌다.
이유가 뭘지 생각해보자.
const task1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Task 1 is completed!');
resolve();
}, 2000);
})
const task2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Task 2 is completed!');
resolve();
}, 500);
})
const task3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Task 3 is completed!');
resolve();
}, 1000);
})
task1
.then(() => task2)
.then(() => task3)
.then(() => console.log('Done.'));
const task1 = () => {
console.log('Task 1 is started.')
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Task 1 is done.');
resolve(1);
}, 2000);
})
};
const task2 = () => {
console.log('Task 2 is started.')
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Task 2 is done.');
resolve(2);
}, 500);
})
};
const task3 = () => {
console.log('Task 3 is started.')
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Task 3 is done.');
resolve(3);
}, 1000);
})
};
task1()
.then(() => task2())
.then(() => task3())
.then(() => console.log('Done'));
결과
좀 더 완성도 있게 해보면 아래와 같이 할 수도 있다.
task1()
.then(() => task2())
.then(() => task3())
.then(() => console.log('Completed!'))
.catch(console.log)
.finally(() => console.log('Done.'));
결과1 (성공)
결과2 (Task 2에서 reject한 경우)
위 코드처럼 프로미스가 연결, 연결 되는 것을 프로미스 체이닝
이라고 한다.
만약 여러 promise를 전부 실행시켜서 결과값을 동시에 가져오고 싶다면
(즉, promise 사이에 dependency가 없다면)
Promise.all 메소드를 사용해야 한다.
Promise.all([task1(), task2(), task3()])
.then((res) => console.log(res))
.catch(console.log)
.finally(() => console.log('Done.'));
결과
1, 2, 3이 전부 실행되고 결과값을 배열로써 받아온 것을 확인할 수 있다.
이때 console.time
함수를 이용해서 시간을 찍어보면
console.time("x");
Promise.all([task1(), task2(), task3()])
.then((res) => {
console.log(res);
console.timeEnd("x");
})
.catch(console.log)
.finally(() => console.log('Done.'));
console.time("x");
Promise.race([task1(), task2(), task3()])
.then((res) => {
console.log(res);
console.timeEnd("x");
})
.catch(console.log)
.finally(() => console.log('Done.'));
Task2 가 완료되자마자 바로 then
메소드가 실행 된 것을 확인할 수 있다.
다른 Task들은 실행이 마저 되기는 하지만 이후 결과는 Promise chaining이 안된다.
만약 가장 처음 끝나는 promise가 reject라도 똑같이 catch구문이 바로 실행된다.