[React #11] Promise / async / await
01. Promise
- 자바스크립트의 비동기를 돕는 객체로, 콜백 지옥을 극복할 수 있음
- 비동기를 쉽고 빠르고 직관적이게 만들 수 있음
- 비동기 처리의 결과값을 핸들링하는 코드를 비동기 함수로부터 분리할 수 있음
- 프로미스를 사용하면 더이상 비동기 처리 함수에 콜백을 줄지어 전달할 필요가 없음
💡콜백 지옥이란?
- 연속되는 비동기 함수들을 처리할 때 비동기 처리의 결과 값을 사용하기 위해 콜백이 계속 깊어지는 현상
- 비동기 처리의 결과를 또 다른 비동기 처리의 결과로 이용하는 로직이 계속 길어지는 현상
step1(function (value1) {
step2(function (value2) {
step3(function (value3) {
step4(function (value4) {
step5(function (value5) {
step6(function (value6) {
step7(function (value) {
consolo.log(value7);
});
});
});
});
});
});
});
✏️ promise의 예시
function isPositiveP(number) {
const executor = (resolve, reject) => {// 실행자
setTimeout(() => {
// 자료형이 숫자일 때만 작업하기기
if (typeof number === "number") {
// 성공했을 때 (resolve)
console.log(number);
} else {
// 실패했을 때 (reject)
}
}, 2000);
};
const asyncTask = new Promise(executor);
return asyncTask;
}
isPositiveP(101);
// 101
📑 비동기 작업을 실질적으로 수행하는 executor의 함수를 실행시키는 방법
const asyncTask = new Promise(executor);
return asyncTask;
1) 비동기 작업 자체인 promise를 저장할 asyncTask를 만듦
2) new 키워드를 사용해서 promise 객체를 생성하면서 생성자로 executor를 넘겨줌
3) 넘겨주면 전달하는 순간 자동으로 executor 함수가 바로 실행이 됨
4) executor를 담은 promise 객체를 asyncTask라는 상수에 담았으므로 이 상수를 return 해줌

* 비동기 작업을 하고 그 결과를 promise 객체로 반환 받아서 비동기 처리의 결과값을 사용할 수 있음
✏️ promise로 받은 객체 사용하기
const res = isPositiveP([]);
res
.then((res) => {
console.log("작업 성공 : ", res);
})
.catch((err) => {
console.log("작업실패 : ", err);
});
- 반환한 프로미스 객체를 res라는 상수에 저장하기
- res는 반환받은 프로미스 객체를 이용해서 비동기 처리에 대한 resolve, reject의 결과값을 아무데서나 사용할 수 있음
- 프로미스 객체의 메서드인 then과 catch를 사용
- 첫 번째로 전달받은 인자는 then에서, 두 번째로 전달받은 인자는 catch에서 결과 값을 받을 수 있음
✏️ Callback과 Promise의 비교
1) Callback
function taskA(a, b, cb) {
setTimeout(() => {
const res = a + b;
cb(res);
}, 3000);
}
function taskB(a, cb) {
setTimeout(() => {
const res = a * 2;
cb(res);
}, 1000);
}
function taskC(a, cb) {
setTimeout(() => {
const res = a * -1;
cb(res);
}, 2000);
}
taskA(3, 4, (a_res) => {
console.log("taskA :", a_res);
taskB(a_res, (b_res) => {
console.log("taskB :", b_res);
taskC(b_res, (c_res) => {
console.log("taskC :", c_res);
});
});
});
2) Promise
function taskA(a, b) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const res = a + b;
resolve(res);
}, 3000);
});
}
function taskB(a) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const res = a * 2;
resolve(res);
}, 1000);
});
}
function taskC(a) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const res = a * -1;
resolve(res);
}, 2000);
});
}
const bPromiseResult = taskA(1, 3).then((a_res) => {
console.log("taskA :", a_res);
return taskB(a_res);
});
console.log("block");
bPromiseResult
.then((b_res) => {
console.log("taskB :", b_res);
return taskC(b_res);
})
.then((c_res) => {
console.log("taskC :", c_res);
});
- then 체이닝 : then 메서드를 계속 이어 붙이는 것
- taskA의 결과값인 a_res를 taskB에 인자로 전달하고 then 메서드를 이용하여 그 결과값인 b_res를 taskC의 인자로 전달하는 형태
- 프로미스는 코드를 계속 아래로 늘여쓸 수 있고 중간에 다른 작업을 끼어넣을 수도 있음
- 프로미스 객체를 이용하면 비동기 처리를 호출 코드와 결과 코드를 분리해서 사용할 수 있음
- 프로미스는 좀 더 가독성 있고 깔끔한 비동기 처리가 가능함
02. async / await
- 비동기를 다루는 기능으로, promise를 더 쉽고 가독성 좋게 작성할 수 있음
1) async
- 함수 앞 부분에 'async'라고 붙이면 자동으로 프로미스를 리턴하는 비동기 처리 함수가 됨
- 프로미스를 반환한다는 것은 호출할 필요가 없고 then을 쓸 수 있다는 뜻
- async를 붙여준 함수는 프로미스의 resolve 값을 리턴함
✏️ async의 예시 코드
function hello() {
return "hello";
}
async function helloAsync() {
return "hello Async";
}
console.log(hello()); // hello
console.log(helloAsync()); // Promise {} (객체 자체)
helloAsync().then((res) => {
console.log(res); // hello Async
});
2) await
- await는 async 키워드가 붙은 함수 내에서만 사용 가능함
- await 함수를 비동기 함수의 호출 앞에 붙이게 되면 비동기 함수가 마치 동기적인 함수처럼 작동을 함
- await 키워드가 붙은 함수의 호출은 뒤에 있는 함수가 끝나기 전까지 아래 있는 코드를 수행하지 않음
✏️ await의 예시 코드
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function helloAsync() {
await delay(3000);
return "hello async";
}
async function main() {
const res = await helloAsync();
console.log(res);
}
main();
① resolve에 콜백 함수 자체를 전달 ✅
setTimeout 콜백 함수 안에 resolve() 호출 말고 다른게 없다면 콜백 함수 자체로 전달해도 됨
- 변경 전
function delay(ms) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, ms);
});
}
- 변경 후
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
② await 사용 ✅
- 변경 전
async function helloAsync() {
return delay(3000).then(() => {
return "hello Async";
});
}
- 변경 후
async function helloAsync() {
await delay(3000);
return "hello async";
}
③ main 함수 안에서 await를 사용하여 호출하기 ✅
- 변경 전
helloAsync().then((res) => {
console.log(res);
});
- 변경 후
async function main() {
const res = await helloAsync();
console.log(res);
}
main();
참고자료
이정환 Winterlood, '한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지'