프로세스의 완료를 기다리지 않고 동시에 다른 작업을 처리하는 방식
특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행!!
//setTimeout(code, delayTime); //delayTime 동안 기다렸다가 코드 실행
console.log(1);
setTimeout(() => {
console.log(2);
}, 2000);
console.log(3);
➡️ 1,3,2 순으로 출력된다. 2는 2초동안 연산이 진행되고 있을 동안에 멈추지 않고
다음 코드(3)이 출력된 것이다.
function goMart() {
console.log('마트에서 어떤 음료수를 살까?! 고민된다');
}
function pickFood() {
setTimeout(() => {
console.log('고민끝');
product = '생크림빵';
price = '3000';
}, 3000);
}
function pay(product, price) {
console.log(`상품명 : ${product}, 가격 : ${price}`);
}
let product;
let price;
goMart();
pickFood();
pay(product, price);
-> (숫자는 생략해서 봐주길..!)
-> 상품명, 가격이 모두 undefined로 나오게 된다..!왤까?
-> 이유는 goMart()
다음에 pickFood()
는 delay가 걸려버리므로 pay 함수가 그냥 바로 실행되어버린다.
-> 그래서 price, product가 undefined
되어버린다.
-> 이를 보완하기 위해 나온 개념이 콜백 함수
이다.
인자(매개변수)로 대입되는 함수
를 콜백함수라고 한다.다른 함수가 실행을 끝낸 뒤 실행되는 함수
함수 타입 파라미터
를 맨 마지막에 하나 더 선언해주는 방식으로 정의📌 왜 사용할까??!?
비동기 방식으로 작성된 함수를 동기 처리 하기 위해서다.
-> 독립적으로 수행되는 작업도 있는 반면, 응답을 받은 이후 처리되어야 하는 종속적인 작업도 있을 수 있으므로 그에 대한 대응방법이 필요하다 -> so, 콜백함수를 사용한다.
//콜백함수 : 함수 타입 파라미터 맨 마지막에 하나 더 선언
// 다른 함수가 실행을 끝낸 후 실행되는 함수
function mainFunc(param1, param2, callback) {
console.log(param1, param2);
callback();
}
function callbackFunc() {
console.log('콜백함수 입니다');
}
mainFunc(1, 2, callbackFunc);
앞선 잘못된 예시를 콜백함수를 이용해서 해결해보자.
///1번 방법
function goMart() {
console.log('마트에서 어떤 음료수를 살까?! 고민된다');
}
function pickFood() {
setTimeout(() => {
console.log('고민끝');
product = '생크림빵';
price = '3000';
pay(product, price);
}, 3000);
}
function pay(product, price) {
console.log(`상품명 : ${product}, 가격 : ${price}`);
}
goMart();
pickFood();
///2번 방법
function goMart() {
console.log('마트에서 어떤 음료수를 살까?! 고민된다');
}
function pickFood(cb) {
//cb = callback함수, 그냥 cb라는 매개변수
setTimeout(() => {
console.log('고민끝');
product = '생크림빵';
price = '3000';
cb(product, price);
}, 3000);
}
function pay(product, price) {
console.log(`상품명 : ${product}, 가격 : ${price}`);
}
goMart();
pickFood(pay); //함수안에 pay함수를 불러오는 것
가독성 감소, 코드 수정 난이도 상승
<script>
//callback hell - 콜백함수가 2,3개정도는 들어갈 수 있지만, 그 이후는 너무 많고 복잡하다.
setTimeout(function () {
document.body.style.backgroundColor = 'red';
setTimeout(function () {
document.body.style.backgroundColor = 'orange';
setTimeout(function () {
document.body.style.backgroundColor = 'yellow';
setTimeout(function () {
document.body.style.backgroundColor = 'green';
setTimeout(function () {
document.body.style.backgroundColor = 'blue';
setTimeout(function () {
document.body.style.backgroundColor = 'navy';
setTimeout(function () {
document.body.style.backgroundColor = 'purple';
}, 2000);
}, 2000);
}, 2000);
}, 2000);
}, 2000);
}, 2000);
}, 2000);
</script>
callback Hell을 해결하기 위해 등장한 것이 Promise
이다.
비동기 함수를 동기 처리하기 위해 만들어진 객체
성공과 실패를 분리해서 반환
비동기 작업이 완료된 후에 다음 작업을 연결시켜 진행할 수 있는 기능을 가진다.
✏️ Promise 상태
두가지 콜백 함수
를 가진다.resolve(value)
: 작업이 성공(fulfilled)한 경우, 그 결과를 value와 함께 호출 -> then
메서드 실행reject(error)
: 에러(rejected)발생 시 에러 객체를 나타내는 error와 함께 호출 -> catch
메서드 실행//promise
function promise1(flag) {
return new Promise((resolve, reject) => {
if (flag) {
// t/f를 나타낼때 쓰는게 flag
resolve('promise 상태는 fulfilled! then으로 연결됩니다. \n 이때의 flag는 true입니다.');
} else {
reject(`promise 상태는 rejected! catch로 연결됩니다.\n 이때의 flag는 false입니다.`);
}
});
}
promise1(false) //true로 하면 resolve, false면 reject
.then(function (result) {
console.log(result); //위의 메시지를 그대로 가져오고 싶기 때문에 함수에 매개변수를 만들어주고 받으면 됌.
})
.catch((error) => {
console.log(error);
});
function goMart() {
console.log('마트에 가서 어떤 음료를 살지 고민중');
}
function pickFood() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('고민끝');
product = '코카콜라';
price = 2000;
resolve(); //성공할때 반환되어야하는게 setTimeout 함수니까.
}, 2000);
});
}
function pay() {
console.log(`상품명 : ${product}, 가격 : ${price}`);
}
goMart();
pickFood().then(pay); //성공할때 then
//(4+3)*2-1 = 13
//1. callback hell
function add(n1, n2, cb) {
setTimeout(() => {
let result = n1 + n2;
cb(result);
}, 1000);
}
function mul(n, cb) {
setTimeout(() => {
let result = n * 2;
cb(result);
}, 700);
}
function sub(n, cb) {
setTimeout(() => {
let result = n - 1;
cb(result);
}, 500);
}
add(4, 3, function (x) {
console.log('1: ', x);
mul(x, function (y) {
console.log('2: ', y);
sub(y, function (z) {
console.log('3: ', z); //이렇게 callback hell이 될 수 있음
});
});
});
(2) 사용한 경우
//promise
function add1(n1, n2) {
return new Promise((resolve, reject) => {
setTimeout(() => {
let result = n1 + n2;
resolve(result);
}, 1000);
});
}
function mul1(n) {
return new Promise((resolve, reject) => {
setTimeout(() => {
let result = n * 2;
resolve(result);
}, 700);
});
}
function sub1(n) {
return new Promise((resolve, reject) => {
setTimeout(() => {
let result = n - 1;
resolve(result);
}, 500);
});
}
add1(4, 3) //함수 안에서 사용할 매개변수
.then((res) => {
console.log('add : ', res); // add : 7
return mul1(res);
})
.then((res1) => {
console.log('mul : ', res1); //mul : 14
return sub1(res1);
})
.then((res2) => {
console.log('sub : ', res2); //sub : 13
});
Promise 체이닝 장점
1. then
메소드를 연속 사용하므로 순차적인 작업이 가능하다.
2. 예외처리가 간편하다. -> 마지막 catch
구문에서 한번에 에러 처리 가능
!
//바로 위의 예시에 동일한 코드에 일부만 추가
function sub1(n) {
return new Promise((resolve, reject) => {
setTimeout(() => {
let result = n - 1;
//resolve(result);
reject(new Error('의도적 에러'));
}, 500);
});
}
add1(4, 3)
.then((res) => {
console.log('add : ', res);
return mul1(res);
})
.then((res1) => {
console.log('mul : ', res1);
return sub1(res1);
})
.then((res2) => {
console.log('sub : ', res2);
})
.catch((err) => {
console.log('실패');
console.log(err);
});
function call(name) {
return new Promise((resolve) => {
setTimeout(function () {
console.log(name);
resolve(name);
}, 1000);
});
}
function back(cb) {
return new Promise((resolve, reject) => {
setTimeout(function () {
console.log('back');
resolve('back');
}, 1000);
});
}
function hell(cb) {
return new Promise((resolve, reject) => {
setTimeout(function () {
resolve('callback hell');
}, 1000);
});
}
call('kim')
.then((res) => {
console.log(res, '반가워');
return back();
})
.then((res1) => {
console.log(res1, '을 실행했구나');
return hell();
})
.then((res2) => {
console.log(`여기는 ${res2}`);
});
기능이 추가된 것이 아니라, Promise를 다르게 사용하는 것이다.
함수 앞
에 붙여 Promise를 반환한다.Promise 앞
에 붙여 Promise가 다 처리될 때까지 기다리는 역할을 하며 결과는 그 후에 반환한다. async function exec() {
//async : await을 감싸고 있는 함수
let user = await call('kim'); //await : promise 기반의 함수에다가 씀
console.log(`${user} 반가워`);
let result = await back();
console.log(`${result}을 실행했구나`);
let result1 = await hell();
console.log(`여기는 ${result1}`);
}
exec();
📝 참고
let exec = async () => {
}
function changeBgcolor(color, delayTime) {
return new Promise((resolve) => {
setTimeout(() => {
resolve((document.body.style.backgroundColor = color));
}, delayTime);
});
}
//delayTime도 각자 다르게 줄 수 있음
async function exec() {
await changeBgcolor('red', 1000);
await changeBgcolor('orange', 3000);
await changeBgcolor('yellow', 2000);
await changeBgcolor('green', 2000);
await changeBgcolor('blue', 1000);
await changeBgcolor('navy', 1000);
await changeBgcolor('purple', 1000);
}
exec();
-> 굉장히 빠르고 쉽게 가능하다!
점차 여러 함수를 쓰며 이전에는 몇십줄씩 작성해야 했던게, 지금은 코드길이가 준게 보인다. 콜백지옥 예제를 async/await로 바꿔보거나, Promise를 이용해서 바꿔봐야겠다.