《 콜백함수 》에 대해 안다는 전제 하에 포스팅합니다.
⚠️ 프로미스는 어려운게 당연한것이니 좌절하지 말자
프로미스란, 비동기 처리에 대해, 처리가 끝나면 알려달라는 ‘약속’이다.
즉, 프로미스를 사용하여 비동기 작업의 동기적 표현을 구현할 수 있는 것이다.
대기(Pending): 프로미스가 생성된 초기 상태
이행(Fulfilled): 프로미스가 성공적으로 완료된 상태이며 성공 리턴 값(resolve) 전달
거부(Rejected): 프로미스가 실패한 상태이며 실패 리턴 값(reject) 전달
Producer (정보 제공)
const promise = new Promise()
const promise = new Promise((resolve, reject) => {
// 무거운 일들을 실행한다.
});
보통 프로미스 안에서 무거운 일들을 실행한다.
프로미스를 출력해보자
const promise = new Promise((resolve, reject) => {
console.log("출력"); // 출력
});
위 코드를 통해 알 수 있는 사실은 프로미스를 만드는 순간 우리가 전달한 executor라는 콜백 함수가 바로 실행되는 것을 확인할 수 있다.
settimeout()을 이용해 원하는 콜백 함수를 2초 뒤에 실행시켜보자
const promise = new Promise((resolve, reject) => {
console.log("출력");
setTimeout(() => {
resolve("sieun");
}, 2000);
});
resolve라는 콜백 함수를 호출하여 기능이 잘 수행됐을 때 "sieun"을 호출하게 하였다.
Consumers(정보 이용)은 then, catch, finally를 이용해 값을 받아올 수 있다.
Resolve(성공리턴값)호출 -> then으로 연결
then을 사용하여 프로미스가 정상적으로 완료가 되면 마지막에 최종적으로 resolve라는 콜백함수를 통해 전달한 값("sieun")이 value에 파라미터로 전달되어져서 들어오는 것을 볼 수 있다.
// Producer
const promise = new Promise((resolve, reject) => {
console.log("출력");
setTimeout(() => {
resolve("sieun");
}, 2000);
});
// Consumers : then, catch, finally
promise.then((value) => {
console.log(value); // (3초 뒤) "sieun"
});
Reject(실패리턴값)호출 -> catch로 연결
reject는 Error라는 object를 통해서 값을 전달한다. 어떤 에러가 발생했는지 이유를 잘 명시해서 작성해줘야한다.
// Producer
const promise = new Promise((resolve, reject) => {
console.log("출력");
setTimeout(() => {
//resolve("sieun");
reject(new Error("네트워크 오류"));
}, 2000);
});
// Consumers : then, catch, finally
promise.then((value) => {
console.log(value);
});
"출력" 실행 후 3초 뒤 네트워크 오류가 발생하는 것을 알 수 있다.
catch를 사용해서 에러가 발생했을 때 어떻게 처리할 것인지 콜백함수를 등록해보자
// Consumers : then, catch, finally
promise
.then((value) => {
console.log(value);
})
.catch((error) => {
console.log(error);
});
더 이상 에러가 발생하지 않고 우리가 받아온 에러가 console.log에 출력되는 것을 볼 수 있다.
위 과정은 Promise 체이닝을 통해 여러 개의 비동기 작업을 순차적으로 실행하고 결과를 처리한다.
// Producer
const promise = new Promise((resolve, reject) => {
console.log("출력");
setTimeout(() => {
//resolve("sieun");
reject(new Error("네트워크 오류"));
}, 2000);
});
// Consumers : then, catch, finally
promise
.then((value) => {
console.log(value);
})
.catch((error) => {
console.log(error);
});
Finally 부분은 성공, 실패하던 상관없이 무조건 마지막에 호출된다.
// Producer
const promise = new Promise((resolve, reject) => {
console.log("출력");
setTimeout(() => {
resolve("sieun");
//reject(new Error("네트워크 오류"));
}, 2000);
});
// Consumers : then, catch, finally
promise
.then((value) => {
console.log(value);
})
.catch((error) => {
console.log(error);
})
// finally 부분 추가로 작성
.finally(() => {
console.log("finally 호출");
});
"출력" 호출 후, 2초 뒤 resolve를 통해 성공 리턴 값인 "sieun" 호출한 뒤에 finall() 호출
즉, 성공하던 실패하던 상관 없이 어떤 기능을 마지막으로 수행하고 싶을 때 finall를 사용하는 것이다.
프로미스를 연결할 때 Promise chaining을 사용한다.
// Promise chaining
const fetchNumber = new Promise((resolve, reject) => {
setTimeout(() => resolve(2), 1000);
});
fetchNumber
.then((num) => num * 2)
.then((num) => num * 3)
.then((num) => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(num - 1), 1000);
});
})
.then((num) => console.log(num));
fetchNumber Promise는 비동기적으로 1초 후에 숫자 2를 반환하고, 이후의 then 메서드 체인에서 각각의 비동기 작업을 순차적으로 처리하여 최종 결과를 출력한다. 자세한 과정은 아래와 같다.
1초 있다가 숫자 2를 전달하는 프로미스를 생성한다.
그 후 프로미스가 정상적으로 실행 되면 첫 번째 then 메서드에서는 이전에 해결(resolve)된 숫자를 가져와 2를 곱한다.
두 번째 then 메서드에서는 이전에 해결된 숫자를 가져와 3을 곱한다.
세 번째 then 메서드에서는 이전에 해결된 숫자를 가져와, 1을 뺀 값을 담은 새로운 Promise 객체를 생성한다. (then은 값을 바로 전달해도 되고 다른 비동기인 프로미스를 전달해도 됨)
마지막 then 메서드에서는 최종적으로 해결된 숫자를 가져와서 출력한다.
프로미스를 체이닝했을 때 어떻게 오류를 해결하까? 예시를 통해 살펴보자
아래 코드는 3가지의 프로미스를 리턴한다.
1초 뒤 암탉 리턴 -> 암탉으로부터 받은 달걀 1초뒤 리턴 -> 달걀을 받아 1초뒤 후라이 리턴
const getHen = () =>
new Promise((resolve, reject) => {
setTimeout(() => resolve("🐔", 1000));
});
const getEgg = (hen) =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(`${hen}=>🥚`, 1000));
});
const cook = (egg) =>
new Promise((resolve, reject) => {
setTimeout(() => resolve(`${egg}=>🍳`, 1000));
});
위 코드를 사용하여 서버에서 닭, 달걀, 요리까지 해보자!!!
getHen() // (가독성을 이유로 여기에 주석을 쓴다.)
.then((hen) => getEgg(hen))
.then((egg) => cook(egg))
.then((meal) => console.log(meal));
콜백함수를 전달할 때 받아오는 값을 다른 함수로 바로 호출하는 경우 생략이 가능하다. (이해x)
getHen() //
.then(getEgg)
.then(cook)
.then(console.log);
만약 댤걀을 받아올 때 네트워크에 문제가 생겨 실패가 되면 어떻게 처리해야할까?
실패시 new Error object 이용
const getEgg = (hen) =>
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error(`error! ${hen}=>🥚`)), 1000);
});
위와 같이 에러 처리를 해주면 아래와 같이 출력이 된다.
어떤 에러 핸들링도 하지 않았기 때문에 에러가 발생한다.
getHen() //
.then(getEgg)
.then(cook)
.then(console.log)
.catch(console.log);
따라서 마지막에 .catch() 메서드를 사용하여 프로미스 체인에서 발생하는 오류를 처리하였다.
프로미스 체인에서 한 단계에서라도 오류가 발생하면, 해당 오류는 체인의 다음 .catch() 메서드로 전달된다.
이를 통해 오류를 적절하게 처리할 수 있는 것이다.
.catch() 메서드를 사용해 달걀을 받아올 때 문제가 생긴다면 다른 재료로 대처하는 에러 핸들링을 처리해보자
getHen() //
.then(getEgg)
.catch(error=>{
return'🧙🏻';
})
.then(cook)
.then(console.log)
.catch(console.log);
계란을 받아오는 것에 문제가 생겨도 전체적인 프로미스 체인에 문제가 발생하지 않도록 처리를 해주었다.
➡️ 이렇게 에러를 처리하고 싶을 떄 .catch() 메서드를 사용하여 바로바로 문제를 해결할 수 있다.
이전에 했던 예시인 콜백함수를 프로미스로 변경해보자!
setTimeout(
function (name) {
var coffeeList = name;
console.log(coffeeList);
setTimeout(
function (name) {
coffeeList += ", " + name;
console.log(coffeeList);
setTimeout(
function (name) {
coffeeList += ", " + name;
console.log(coffeeList);
setTimeout(
function (name) {
coffeeList += ", " + name;
console.log(coffeeList);
},
500,
"카페라떼"
);
},
500,
"카페모카"
);
},
500,
"아메리카노"
);
},
500,
"에스프레소"
);
new Promise(function (resolve) {
setTimeout(function () {
var name = '에스프레소';
console.log(name);
resolve(name);
}, 500);
}).then(function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var name = prevName + ', 아메리카노';
console.log(name);
resolve(name);
}, 500);
});
}).then(function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var name = prevName + ', 카페모카';
console.log(name);
resolve(name);
}, 500);
});
}).then(function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
var name = prevName + ', 카페라떼';
console.log(name);
resolve(name);
}, 500);
});
});
var addCoffee = function (name) {
// 변수 이름을 name으로 받음
return function (prevName) {
return new Promise(function (resolve) {
setTimeout(function () {
// 백틱
var nameName = prevName ? `${prevName}, ${name}` : name;
console.log(name);
resolve(nameName);
}, 500);
});
};
};
addCoffee("에스프레소")()
.then(addCoffee("아메리카노"))
.then(addCoffee("카페모카"))
.then(addCoffee("카페라떼"));