이 글은 드림코딩by엘리 님의 영상을 참고, 학습하여 작성한 글입니다.
먼저 Promise에 대해 간단히 알아보자
Promise는 비동기 처리를 위한 객체이다.
비동기 작업의 성공 혹은 실패를 나타내는 객체라고 한다. (from MDN)
콜백지옥을 방지하기 위해 사용할 수 있다.
Promise를 통해 만들어진 객체는 생성될 때 내부의 함수를 자동적으로 실행한다.
그리고 나서 실행할 것들을 .then() 과 .catch() 를 이용하여 처리한다.
기본적인 flow는 이렇다.
resolve나 reject가 일어나기 전에는 pending 상태를 유지하다가
둘 중 어떤 값이 전달되면 fulfilled 혹은 rejected 상태가 되고 각 값을 리턴한다.
Promise를 활용한 비동기 처리의 간단한 예시이다.
const promise = new Promise((resolve, reject) => {
// Promise 객체는 생성되었을 때 () 내부 함수를 자동적으로 실행시킨다!
setTimeout(() => {
resolve('promise object created')
}, 2000);
})
// promise 객체 내부 함수가 끝나면 전달 받은 값을 출력
promise.then(console.log);
// 1초 후 출력
setTimeout(() => {
console.log('set time out done');
}, 1000);
console.log('the last log');
결과 값은
/*
the last log
set time out done
promise object created
*/
이렇게 나오게 된다.
'the last log' 가 가장 마지막에 배치되었지만
동기적인 순서로는 먼저 있던 실행 문장들이 setTimeout으로 늦어져 비동기 처리가 일어났다.
결국 가장 먼저 써있었지만 가장 늦게 처리가 된 promise 객체의 출력값이 가장 나중에 배치되었다.
React에서 보통 쓰이는 .then()이 여기서 나온 듯 하다.
그 다음은 promise에서 한단계 더 발전한 async, await 을 알아보자
promise를 promise chain으로 엮어서 콜백 지옥처럼 나타나는 표현식을
동기적인 표현식처럼 적을 수 있다.
하지만 무조건 promise 보다 async, await 이 더 좋은 것만은 아니라고 한다!!
아래의 예시를 보자
비동기 병렬 처리를 이용하여
과일이 뽑혔다는 문장을 출력해주는 예시이다.
// "Promise 객체"를 통한 값을 전달하는 함수
function delay(dTime) {
// Promise를 이용해 dTime 만큼의 시간이 지난 후 resolve를 통해 dTime 값을 리턴
return new Promise((resolve)=>{
// setTimeout으로 dTime 만큼의 시간 delay를 건다
setTimeout(() => {
resolve(dTime);
}, dTime)
});
}
먼저 delay 라는 Promise 객체를 이용한 함수를 만들어준다.
setTimeout을 이용하여 딜레이를 걸어주고 딜레이 시간값을 resolve로 리턴한다.
// 비동기 처리를 위한 함수 getApple
async function getApple(aTime) {
const appleTime = await delay(aTime); // aTime 값 만큼의 딜레이를 await 해줌
console.log(appleTime); // 그리고 지난 시간 출력
return 'Apple!' // 최종적으로 "Apple!"" 리턴
}
// 비동기 처리를 위한 함수 getBanana
async function getBanana(bTime) {
const bananaTime = await delay(bTime); // bTime 값 만큼의 딜레이를 await 해줌
console.log(bananaTime); // 그리고 지난 시간 출력
return 'Banana!!' // 최종적으로 "Banana!!" 리턴
}
그리고 각 과일 string 을 최종적으로 리턴해주는 비동기 함수를 생성한다.
async, await 을 이용하여 딜레이 시간만큼 기다려준 후
기다림이 끝나면 콘솔에 걸린 시간을 출력하고
각각 Apple! 과 Banana!! 를 리턴한다.
// 비동기 처리를 위한 함수
async function pickFruits() {
// 미리 과일+Promise 변수에 할당을 해놓지 않으면
// "[object] + [object] has picked" 가 먼저 출력되고 숫자들이 출력됨
const applePromise = getApple(5000);
const bananaPromise = getBanana(2000);
// await을 해주지 않으면
// 병렬 처리가 되지 않아 총 7000 ms가 걸리게 된다
const apple = await applePromise;
const banana = await bananaPromise;
// apple과 banana에 리턴된 값을 문장으로 만들어 리턴
return `${apple} + ${banana} has picked`
}
// 최종 리턴값을 then의 console.log를 통해 출력
pickFruits().then(console.log);
마지막으로 과일을 고르는 함수를 만든다.
동일하게 비동기를 이용할 것이므로 async를 적어주고
각각의 과일을 리턴하는 함수를 원하는 딜레이 시간만큼 걸리게 변수에 할당해준다.
주석으로도 적어놨지만 미리 과일+Promise 변수에 할당하지 않고
const apple = getApple(5000)
바로 이런 식으로 사용하면
원하는 함수에서 리턴값이 나오기 전에 pickFruits 함수의 리턴값이 먼저 나와버리므로
[objectPromise] + [objectPromise] has picked 가 출력된다.
const apple = await getApple(5000)
그럼 이렇게 쓰면 되지 않냐고 할 수 있는데
그렇게 실행하면 비동기 병렬처리가 아니라 동기식으로 처리가 된다.
따라서 7초 후에 원하는 출력값이 전부 나오게 된다.
미리 promise 객체를 사용하는 함수를 변수에 할당해버리면
promise 객체가 만들어지면서 함수가 바로 실행되기 때문에
변수할당시 병렬적으로 처리가 일어난다.
그리고 나서 사용할 변수인 apple, banana에 await으로 과일+promise를 할당해주면서
다시 동기화를 시켜주는 것이다!
결국 과일+promise가 promise내부의 함수 실행이 끝나면
원하는 값이 리턴된다.
따라서 5000ms가 지나면 최종적으로 ${apple} + ${banana} has picked 를 리턴한다.
결국 출력값은
/*
2000
5000
Apple! + Banana!! has picked
*/
이렇게 나오게 된다.
첫 이해는 좀 어렵지만,
네트워크는 비동기 처리가 중요하기 때문에 숙달되면 정말 유용할 것이다.