0. 들어가며 : 비동기 처리
- 비동기 처리란 ‘특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드를 먼저 수행하는 자바스크립트의 특성’
e.g) 값을 호출하는 중에 출력해버려서 값이 undefined로 나옴.
- 비동기 처리가 필요한 이유는 프론트에서 서버로 데이터를 요청했을 때 서버가 언제 그 요청에 답을 줄 지 모르는데 마냥 다른 코드들 멈춰놓고 기다릴수가 없기 때문. 하나씩 완료되고 처리 완료되고 처리 하면 아마 웹 애플리케이션을 실행하는데 수십 분은 걸릴 것.
- Promise는 비동기 처리를 위해 사용되는 객체
1. Promise 선언
- 지금은 없는데 나중에 완료되면 실행하고 오류나면 따로 알려줄게요~ 라고 '약속'
- 다른거 먼저 실행하세요 Promise는 완료되면 실행할게요.
// Promise
const promiseTest = new Promise((resolve, reject)=>{resolve()})
- resolve() : 성공한 경우, 반환
- reject() : 실패한 경우, 반환
- Promise 작업은 3가지 상태가 있음
- Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
- Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
- Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태
2. Promise 실행 (then / catch / finally)
1) Promise 세팅 후
2) promise걸린변수명.then(function(){}).catch(function(){}).finally(function(){})
- then은 성공시 실행할 것
- catch는 실패시 실행할 것
- finally는 성공이든 실패든 마지막에 실행할 것
📌 성공만 세팅했을 때
// Promise
const proTest = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('OK')
}, 3000)
});
console.log(`시작`)
proTest.then(
function(result){
console.log(result)
}
).catch(
function(err){
console.log(err)
}
).finally(
function(){
console.log(`성공이던 실패던 실행완료시 출력`)
}
)
---------- 실행 결과 --------------
시작
OK // 시작 나오고 3초뒤 출력
성공이던 실패던 실행완료시 출력 // OK와 함께 출력
📌 실패만 세팅했을 때
- 새로운 에러 띄우려면 new Error("에러 발생시 띄울 말") 이렇게 작성
// Promise
const proTest = new Promise((resolve, reject)=>{
setTimeout(()=>{
reject(new Error("err...."))
}, 3000)
});
console.log(`시작`)
proTest.then(
function(result){
console.log(result)
}
).catch(
function(err){
console.log(err)
}
).finally(
function(){
console.log(`성공이던 실패던 실행완료시 출력`)
}
)
---------- 실행 결과 --------------
시작
Error: err....
at Timeout._onTimeout (c:\Users\user\Desktop\폴더\코딩연습\practice\main.js:4:16)
at listOnTimeout (node:internal/timers:569:17)
at process.processTimers (node:internal/timers:512:7)
성공이던 실패던 실행완료시 출력
📌 성공, 실패 함께 세팅하려면 : if 문 활용
// if 를 활용하여 resolve, reject 함께 조건 설정할 수 있다.
const arr = [1,2,3,4,5]
const getEgg = () => {
return new Promise((resolve, reject) => {
if (arr.length === 0) {
reject("아무도 없다")
} else
(setTimeout(() => {
resolve('egg')
}, 1000))
})
}
📌 연습
// Promise 연습
const test = function () {
return new Promise((resolve, reject) => {
resolve("성공")
})
}
const test1 = function (something) {
return new Promise((resolve, reject) => {
resolve(`${something}했다`)
})
}
const test2 = function (something2) {
return new Promise((resolve, reject) => {
resolve(`${something2}구요`)
})
}
test()
.then(test1)
.then(test2)
.then(console.log)
.catch(error => new Error("err..."))
.finally(() => console.log("만세"))
----------결과----------------
성공했다구요
만세
3. 콜백 함수 (callback) : 함수의 인수를 '함수'로 받는 것을 뜻 함.
- 콜백 함수를 이용하면 특정 로직이 끝났을 때 원하는 동작을 실행시킬 수 있음 (즉, 비동기 처리의 문제점을 해결)
const f1 = (callback1) => {
setTimeout(() => {
console.log("1번 완료")
callback1(); // 여기서 인수로 받은 콜백함수를 1번완료 출력 이후에 실행하도록 함.
}, 1000);
};
- 콜백 지옥 (Callback Hell) : 비동기 함수들의 중첩 실행 구조
1번완료하고 1번을 인수로 받아서 2번, 2번완료하고 2번을 인수로 받아서 3번 ... 뎁스가 깊어지며 계속해서 콜백함수를 불러오는 구조
// Callback Hell
// 1번완료 로그 찍고 인수로 받은 callback 함수를 실행해라
// f1(콜백함수); 실행하면 1번 완료 찍히고 콜백함수 실행
// f1의 인수로 f2를 하고 f2의 인수로 f3을 하고 ~~~ 쭉쭉 콜백헬
const f1 = (callback1) => {
setTimeout(() => {
console.log("1번 완료")
callback1();
}, 1000);
};
const f2 = (callback2) => {
setTimeout(() => {
console.log("2번 완료")
callback2();
}, 1000);
};
const f3 = (callback3) => {
setTimeout(() => {
console.log("3번 완료")
callback3();
}, 1000);
};
console.log("start")
f1(function () { // '1번 완료' 출력 후에 인수로 받은 함수(f2)를 실행
f2(function () { // '2번 완료' 출력 후에 인수로 받은 함수(f3)를 실행
f3(function () { // '3번 완료' 출력 후에 인수로 받은 함수(console.log("end")를 실행
console.log("end");
});
});
});
4. 프로미스 체이닝 Promises Chaining
- 콜백헬 대체 콜백헬처럼 프로미스가 연결연결 되는 것을 뜻함.
- then() 메소드는 Promise 객체를 '반환'해야 한다.
- return 을 잘 작성해주자.
// Promise 체이닝
const getEgg = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('egg')
}, 1000)
})
};
const getChicken = item => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${item} become Chicken`)
}, 1000)
})
};
const getBBQ = (item) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${item} become BBQ CHICKEN`)
}, 1000)
})
};
getEgg()
.then(겟에그결과값 => getChicken(겟에그결과값)) // 겟 에그 성공하면 ~ 결과값을 인수로 삼아 겟 치킨 함수 실행
.then(겟치킨결과값 => getBBQ(겟치킨결과값))
.then(겟비비큐결과값 => console.log(겟비비큐결과값))
.catch(error => console.log(error))
// 3초 뒤
egg become Chicken become BBQ CHICKEN
// 아래처럼 생략할 수도 있음.
getEgg()
.then(getChicken)
.then(getBBQ)
.then(console.log)
.catch(error => console.log(error))
5. Promises.all : 함수 병렬로 실행하고 모두 완료되면 한번에 출력
- 하나의 요소라도 누락되면 페이지를 보여주면 안되는 경우에 사용 (전부 완료됐을 때 보여주거나, 아예 안보여주거나)
- Promise.all() 메소드는 전달된 Promise 객체들이 모두 resolve될 때까지 기다린 후, 모든 Promise 객체의 결과값을 배열로 반환합니다.
- 배열을 인자로 받아 각각 병렬로 실행하고 완료되면 한번에 출력
- 그래서 Promise Chaining이 안됨 ★
- Promise.all([함수 배열]).then((완료되면 받는 인수)=>{그 인수 활용 완료되면 할 것})
// Promise.all
const getHen = () => {
console.log("chicken완료")
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('chicken')
},1000)
})
}
const getEgg = item => {
console.log("egg완료")
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve(`${item} => egg`)
},5000)
})
}
const cook = item =>{
console.log("요리 완료")
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve(`${item} => friedegg`)
},1000)
})
};
Promise.all([getHen(), getEgg(), cook()]).then((item) => {
console.log(item);
})
chicken완료 // 실행하자마자 출력
egg완료 // 실행하자마자 출력
요리 완료 // 실행하자마자 출력
[ 'chicken', 'undefined => egg', 'undefined => friedegg' ] // 함수 중 가장 처리속도가 늦은 5초 후에 출력 (체이닝 안됨)
6. Promises.race : 함수 병렬로 실행하고 모두 완료되면 한번에 출력
- 사용방법은 Promise.all과 동일 [배열로 함수를 받음]
- all 과 차이점은 all은 모든 작업이 끝날 때 까지 기다리지만, race는 '경주'이다. 하나라도 1등으로 완료된게 있으면 반환하고 끝냄.
- 중간에 reject 에러가 있어도 먼저된게 반환 (에러가 먼저되면 에러반환)