본 게시글은 한입 크기로 잘라 먹는 리액트 (인프런) 강의를 보고 정리한 내용입니다.
비동기 작업이 가질 수 있는 3가지 상태
- Pending 대기상태 : 현재 비동기 작업이 진행 중 혹은 시작할 수 없는 문제가 있는 상황.
- Fulfilled 성공
- Rejected 실패
비동기 작업은 한 번 성공하거나 실패하면, 그것으로 끝이 난다.
- Pending → Fulfilled : Resolve 해결
- Pending → Rejected : Reject 거부
성공과 실패를 이렇게 두 가지 경우로 볼 수 있다.
비동기 처리 결과 핸들링 : 콜백 함수 버전
- 콜백 함수를 활용하여 비동기 처리의 결과를 성공 resolve, 실패 reject 로 핸들링. (promise 객체 사용 x)
function isPositive(number, resolve, reject) {
setTimeout(() => {
if (typeof number === "number") {
//성공 => resolve
resolve(number >= 0 ? "양수" : "음수");
} else {
//실패 => reject
reject("주어진 값이 숫자형 값이 아님.");
}
}, 2000);
}
isPositive(
10,
(res) => {
console.log("성공적으로 수행됨:", res);
},
(err) => {
console.log("실패하였음:", err);
}
);
- isPositive 함수를 실행하며 순서대로, number, resolve 콜백함수, reject 콜백함수를 전달하고 있다.
- isPositive 함수 내부에서는 setTimeout 비동기 함수가, number 매개변수의 type이 'number'일 경우를 성공으로
그렇지 않은 경우 실패로 보고 전달된 resolve, reject 콜백함수에 결과값을 전달하여 실행한다. - 각 콜백함수에서는 성공 결과를 res로, 실패 결과를 err 로 전달 받아 출력한다.
비동기 처리 결과 핸들링 : promise 버전
- 같은 작업에 promise 객체를 활용한 코드는 아래와 같음.
function isPositiveP(number) {
// 비동기 작업을 실제로 수행할 executor 함수
const executor = (resolve, reject) => {
setTimeout(() => {
if (typeof number === "number") {
//성공 => resolve
resolve(number >= 0 ? "양수" : "음수");
} else {
//실패 => reject
reject("주어진 값이 숫자형 값이 아님.");
}
}, 2000);
};
// 프로미스 객체의 생성자로 방금 만든 비동기 힘수를 전달
// 그 값을 상수 asyncTask에 저장하였음
// 이렇게 저장하는 순간 executor 함수는 바로 실행된다.
const asyncTask = new Promise(executor);
// 프로미스를 반환한다 : 이 함수는 비동기 함수이다라는 의미.
return asyncTask;
}
// promise 객체를 사용한 비동기 함수의 결과값을 저장하고,사용한다.
const res = isPositiveP(10);
res
.then((res) => {
console.log("작업 성공: ", res);
})
.catch((err) => {
console.log("작업 실패: ", err);
});
- promise 객체를 리턴하는 함수의 결과값을 then 과 catch 메서드로 활용할 수 있다.
- res가 함수 isPositive 에 숫자 10을 넣어 호출한 결과 값이고, isPositive 함수는 내부 로직에 따라 resolve 혹은 reject를 처리한 뒤 이 결과값을 promise 객체인 asyncTask 에 담고 이를 리턴하고 있다.
- 즉 res 는 프로미스 객체이고, 처리 결과가 resolve 상태일 때 then 메서드를 사용해 비동기 처리 결과값을 받을 수 있다.
- 만약 reject 상태라면 catch 메서드에서 실패한 결과값을 받을 수 있다.
프로미스 체이닝 : 여러개의 프로미스 연결해서 사용하기
- 3개의 비동기 처리 함수 taskA,B,C 가 있을 때 이를 promise를 사용해 작성하면 아래와 같음
function taskA(num1, num2) {
// resolve, reject 콜백 함수를 인자로 받는 생성자 함수를
// promise 객체에 바로 전달하고 이 결과값을 리턴하고 있다.
// 이렇게 리턴한 promise 객체에서 then(), catch()로 그 결과값을 이용할 수 있다.
return new Promise((resolve, reject) => {
setTimeout(() => {
const res = num1 + num2;
resolve(res);
}, 3000);
});
}
function taskB(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const res = num * 2;
resolve(res);
}, 1000);
});
}
function taskC(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const res = num * -1;
resolve(res);
}, 2000);
});
}
then 체이닝 잘못된 사용 예시
- 아래 코드처럼 사용하면 콜백을 전달하는 콜백 지옥과 다를 것이 없다. 계속 안으로 들여써지고 있는 것이 보일 것임...
taskA(5, 1).then((a_res) => {
console.log(`taskA 결과값 ${a_res}`);
taskB(a_res).then((b_res) => {
console.log(`taskB 결과값 ${b_res}`);
taskC(b_res).then((c_res) => {
console.log(`taskC 결과값 ${c_res}`);
});
});
});
then 체이닝의 올바른 사용 예시
// taskA promise를 실행하고, taskB를 리턴 => 리턴 값도 프로미스 객체
// 이를 then 메서드로 계속 연결하여 사용할 수 있다.
// !then 체이닝! : 콜백 지옥 없이 비동기 처리의 다른 비동기 처리의 전달값으로 쓸 수 있다.
const bPromiseResult = taskA(5, 1).then((a_res) => {
console.log(`taskA 결과값 ${a_res}`);
return taskB(a_res);
});
bPromiseResult
.then((b_res) => {
console.log(`taskB 결과값 ${b_res}`);
return taskC(b_res);
})
.then((c_res) => {
console.log(`taskC 결과값 ${c_res}`);
});
SUMMARY
- promise 객체를 사용하면 콜백 지옥(비동기 처리 결과를 다른 비동기 함수의 전달 인자로 쓰기 위해 콜백을 계속 연결하다보면 엄청난 들여쓰기로 가독성이 굉장히 떨어진다.) 을 피하면서 비동기 작업을 할 수 있다.
- 비동기 작업이 성공한 상태를 reject, 실패한 상태를 resolve 로 볼 수 있고, 이 두 콜백 함수를 인자로 받는 함수를 생성자로 promise 객체를 생성한다.
- reject 상태의 결과값을 then 메서드로, resolve 즉 실패 상태의 결과값을 catch 로 받아 사용할 수 있다.
- promise의 장점은 하나의 프로미스를 변수에 저장해두고 다른 작업을 한 뒤 다시 이어서 사용 할 수 있다는 것이다. (결과값에 then 메서드를 붙여 활용할 수 있다.)
아직 비동기 함수와 프로미스의 실제 사용 상황을 경험한 적이 없어서 잘 와닫지가 않는다. 최대한 정리를 해보았는데 틀린 내용이 있을 수도 있다. 대신 오늘 이렇게 정리했으니 다음에 비동기와 프로미스에 대한 내용을 더 깊게 공부할 때는 조금 더 쉽게 이해할 수 있을 것 같다.
참고자료
https://joshua1988.github.io/web-development/javascript/promise-for-beginners/
'JavaScript > JavaScript' 카테고리의 다른 글
[JavaScript] 객체지향 프로그래밍 : OOP란 무엇인가? (0) | 2022.07.22 |
---|---|
[JavaScript] 클로저와 커링 초간단 정리 (0) | 2022.07.21 |
[JavaScript] 동기와 비동기 (0) | 2022.07.16 |
[JavaScript]DocumentFragment 와 HTML5 template 태그 사용 (0) | 2022.07.14 |
DOM : Document Object Model (0) | 2022.07.14 |