JavaScript에서 Promise :
비동기 작업의 완료 또는 실패를 나타내는 객체
비동기 작업이 성공하면 작업의 결과 값으로, 실패하면 오류 이유로 Promise 객체를 사용할 수 있음
Promise는 콜백 대신 비동기 작업을 처리하는 더 나은 방법을 제공함
세가지 주요 상태
- Pending (대기 중): 초기 상태, 비동기 작업이 아직 완료되지 않음.
- Fulfilled (이행됨): 비동기 작업이 성공적으로 완료됨.
- Rejected (거부됨): 비동기 작업이 실패함.
let promise = new Promise((resolve, reject) => {
// 비동기 작업 수행
let success = true;
if (success) {
resolve("작업 성공");
} else {
reject("작업 실패");
}
});
Promise 객체는 생성자 함수 Promise를 사용하여 생성할 수 있음
이 생성자는 resolve와 reject 함수를 인수로 받는 콜백 함수를 인수로 받음
promise
.then(result => {
console.log(result); // "작업 성공"
})
.catch(error => {
console.error(error); // "작업 실패"
})
.finally(() => {
console.log("작업 완료");
});
- then 메서드: 작업이 성공했을 때 호출될 콜백 함수를 인수로 받음
- catch 메서드: 작업이 실패했을 때 호출될 콜백 함수를 인수로 받음
- finally 메서드: 작업의 성공 또는 실패 여부에 상관없이 항상 실행될 콜백 함수를 인수로 받음
Promise 체이닝
function fetchData1() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve("데이터1 가져오기 성공"), 1000);
});
}
function fetchData2() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve("데이터2 가져오기 성공"), 1000);
});
}
fetchData1()
.then(result => {
console.log(result);
return fetchData2();
})
.then(result => {
console.log(result);
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log("작업 완료");
});
여러 비동기 작업을 순차적으로 수행하려면 then 메서드를 체이닝할 수 있음
예시 ▼ 2초 후에 무작위로 성공 또는 실패를 시뮬레이션
let asyncTask = new Promise((resolve, reject) => {
setTimeout(() => {
let success = Math.random() > 0.5;
if (success) {
resolve("작업 성공");
} else {
reject("작업 실패");
}
}, 2000);
});
asyncTask
.then(result => {
console.log(result);
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log("작업 완료");
});
▼ 위와 같은 예시이지만 async와 await을 사용함
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve("데이터 가져오기 성공");
} else {
reject("데이터 가져오기 실패");
}
}, 2000);
});
}
async function fetchDataAsync() {
try {
const result = await fetchData();
console.log(result);
} catch (error) {
console.error(error);
} finally {
console.log("작업 완료");
}
}
fetchDataAsync();
이 예제에서 fetchDataAsync 함수는 async로 정의되어 있음
await 키워드를 사용하여 fetchData 함수가 반환하는 Promise가 이행될 때까지 기다리게 됨
try, catch, finally 블록을 사용하여 비동기 작업의 결과를 처리
async/await를 사용하면 비동기 코드를 더 읽기 쉽고, 동기 코드처럼 보이게 작성할 수 있음
- async : 함수 선언 앞에 사용되며, 해당 함수가 비동기 함수임을 나타냄(이 함수는 항상 Promise를 반환)
- await : Promise가 해결되거나 거부될 때까지 함수 실행을 일시 중지(await는 async 함수 안에서만 사용할 수 있음)
왜 굳이 동기 코드처럼 보이게 작성할까?
async/await를 사용하여 비동기 코드를 동기 코드처럼 보이게 작성하는 데에는 몇 가지 주요 이점이 있습니다:
가독성과 유지보수성 향상
1. 직관적인 흐름: async/await를 사용하면 코드가 위에서 아래로 읽히며, 명령문이 순차적으로 실행되는 것처럼 보입니다. 이는 비동기 코드의 복잡성을 줄이고, 코드의 흐름을 더 쉽게 이해할 수 있게 합니다.
async function process() {
try {
const data1 = await fetchData1();
const data2 = await fetchData2();
console.log(data1, data2);
} catch (error) {
console.error(error);
}
}
2. 에러 처리 간소화: try/catch 블록을 사용하여 동기 코드에서처럼 비동기 코드에서도 에러를 처리할 수 있습니다. Promise 체이닝에서 .catch()를 여러 번 사용하는 것보다 더 간단하고 직관적입니다.
async function fetchDataAsync() {
try {
const result1 = await fetchData1();
const result2 = await fetchData2();
console.log(result1, result2);
} catch (error) {
console.error(error);
}
}
콜백 지옥 (Callback Hell) 해결
복잡한 중첩 제거: Promise 이전에는 비동기 코드를 작성할 때 콜백 지옥(Callback Hell)이라고 불리는 코드 중첩 문제가 발생했습니다. Promise로 개선되었지만, 여전히 복잡한 체이닝이 필요한 경우에는 코드가 복잡해질 수 있습니다. async/await는 이러한 문제를 완화합니다.
// Callback Hell 예시
fetchData1((data1) => {
fetchData2(data1, (data2) => {
fetchData3(data2, (data3) => {
console.log(data3);
});
});
});
// Promise 체이닝
fetchData1()
.then(data1 => fetchData2(data1))
.then(data2 => fetchData3(data2))
.then(data3 => console.log(data3))
.catch(error => console.error(error));
// async/await
async function fetchDataAsync() {
try {
const data1 = await fetchData1();
const data2 = await fetchData2(data1);
const data3 = await fetchData3(data2);
console.log(data3);
} catch (error) {
console.error(error);
}
}
코드 유지보수와 디버깅 용이성
- 코드 유지보수: 코드가 간결하고 명확하면 유지보수가 더 쉬워집니다. 개발자는 동작 방식을 쉽게 이해하고, 필요한 수정 사항을 신속하게 적용할 수 있습니다.
- 디버깅: async/await는 디버깅이 더 쉽습니다. 디버깅할 때 비동기 호출이 마치 동기 호출처럼 한 줄씩 실행되기 때문에, 실행 흐름을 따라가기가 더 수월합니다.
비동기 작업 간의 의존성 처리
비동기 작업 간에 의존성이 있는 경우, async/await는 이를 더 명확하게 처리할 수 있습니다. 예를 들어, 두 번째 비동기 작업이 첫 번째 작업의 결과를 필요로 하는 경우, async/await를 사용하면 이를 더 명확하게 표현할 수 있습니다.
async function process() {
const result1 = await fetchData1();
const result2 = await fetchData2(result1); // result1이 필요함
console.log(result2);
}
//일반함수
function add1(a, b) {
return a + b;
}
const result = add1(10, 10);
console.log(`더하기 결과: ${result}`);
//화살표함수
const add2 = (a, b) => {
return a + b;
}
//콜백함수
const add3 = (a, b, callback) => {
const result = a + b;
callback(result);
}
add3(10, 10, (result) => {
console.log(`콜백함수 안에서의 더하기 결과: ${result}`);
})
//콜백함수
// const divide = (a, b, callback) => {
// const result = a / b;
// callback(result);
// }
// divide(100, 0, (result) => {
// console.log(`콜백함수 안에서의 나누기 결과: ${result}`);
// }) //Infinity가 출력됨 -> 에러라고 표시하고싶은데..
const divide1 = (a, b, callback) => {
if (b == 0) {
callback('두번째 값이 없어용', null);
return;
}
const result = a / b;
callback(null, result);
}
divide1(100, 0, (err, result) => {
if (err) {
console.error(`에러발생: ${err}`);
return;
}
console.log(`콜백함수 안에서의 나누기 결과: ${result}`);
})
//아 이거 너무 복잡한데...
//콜백함수를 쓸수밖에ㅓㅄ는 상황에서는 어짜피 써야함
//setTimeout 같은 함수
//브라우저에서 요청 -> 웹서버 응답
//1.장비랑케이블이엄청연결되어잇음 중간에장비를엄청마니거침
//중간에장비성능떨어지면응답이언제올지몰라
//응답을받아서콜백함수를실행한다고할때 콟백이언제실행될지알수강벗ㄷ어
//2.관계형DB 테이블로만드는이유 찾기편하게하기위해
//db가 실제로는 서버처럼 요청가능 응답받을수잇음
//응답이언제올지몰라
//예시를 만들어보자
const divide2 = (a, b, callback) => {
setTimeout(() => {
if (b == 0) {
callback('두번째 값이 없어용', null);
return;
}
const result = a / b;
callback(null, result);
}, 1000)
}
divide2(100, 10, (err, result) => {
if (err) {
console.error(`에러발생: ${err}`);
return;
}
console.log(`콜백함수 안에서의 나누기 결과: ${result} 68라인`);
})
const add4 = (a, b, callback) => {
setTimeout(() => {
const result = a + b;
callback(null, result);
}, 500);
}
add4(10, 10, (err, result) => {
console.log(`콜백함수 안에서의 더하기 결과: ${result} 77라인`);
})
//출력결과가 77라인이 먼저 나옴
//순서대로 실행되야하는게 맞지만 setTimeout으로 인해 시간 차이가 생겨버림
//68라인:1초뒤 | 77라인:0.5초뒤
//이래서 서버 응답이 순서대로 오지 않을 수도 있다
//
//리턴은 결과가 아래쪽ㄱ구멍으로 떨어지게 만들어줌 -> 결과가 나올때까지 대기함
//대기하지않도록하게만들어주는방법이 콜백함수
//웹서버는 요청이 들어오는데로 큐 에 쌓아둠
//언제요청햇는지에따라순서대로처리
//그과정이 성능이 너무떨어짐
//요청들어와? 실행먼저해 들어오는데로 걍 실행해(콜백등록해)
//각각ㅇㅣ결과가만들어지면그때응답해
//나누기결과를가지고더해주고싶다면?
divide2(100, 10, (err, result) => {
if (err) {
console.error(`divide2 에러발생: ${err}`);
return;
}
console.log(`콜백함수 안에서의 나누기 결과: ${result}`);
add4(result, 10, (err, result2) => {
if (err) {
console.error(`divide2 add4 에러발생: ${err}`);
return;
}
console.log(`콜백함수 안에서의 더하기 결과: ${result2}`);
})
})
//다른 개발자가 만든거 우리는 사용만 할 수 잇어
//더하기나 나누기 결과가 언제올지몰라
//근데 나눈결과로 더해야해
//그럼 이런 형태가 나올수밖에없음
//
//순서대로하고싶으면 콜백 안에 다른 함수를 넣을 수 밖에 없음
//계속 들여쓰기되는 형태
//들여쓰기가 10개까지 늘어나는 상황이 있을 수 있음
//이거 너무 별론데 정리 함 해야겠는데
//
//정리 함 해주자고
// Promise -> 사용을 최대한 단순하게 만든 방법 -> async awite
//
//결론:
//응답이 언제 올지모르는상황이 웹서버에서는 빈번하다
//그 응답을 순서대로 처리해주고 싶다
//그럴때 사용하는 async awite <- 꼭 알아둬야 함!
// Promise 사용해보기
//복잡해보이지만 간단해지고있는 과정
const addFunc = (a, b) => new Promise((resolve, reject) => {
add4(a, b, (err, result) => {
if (err) {
reject(err);
return;
}
resolve(result);
})
})
// Promise는 중괄호 없어도 됨
const divideFunc = (a, b) => new Promise((resolve, reject) => {
divide1(a, b, (err, result) => {
if (err) {
reject(err);
return;
}
resolve(result);
})
})
// 그러면 이렇게 간단하게 바뀔수있음
const doCalc = async () => {
try {
const result1 = await divideFunc(200, 10); //콜백이아니게됨
console.log(`나눈결과: ${result1}`);
const result2 = await addFunc(result1, 10);
console.log(`더한결과: ${result2}`);
}
catch (err) {
console.error(`doCalc에서 에러 발생: ${err}`);
}
}
doCalc();
'Language > Javascript' 카테고리의 다른 글
[JS] 모듈을 불러오는 두가지 방식 - import, export / require (0) | 2024.08.21 |
---|---|
[JS] 현재 시간 띄우기 (0) | 2024.07.25 |
[JS] 객체와 배열 (0) | 2024.07.25 |
[JS] <input type="radio"> 선택한 값 활용하기 (0) | 2024.07.25 |
[JS] 상속 (0) | 2024.07.25 |