✨ 서론
function first() {
let value;
setTimeout(() => {
value = {name: 'max', age: 18};
}, 3000);
return value;
}
console.log(first()); // undefined
- 변수 value에 객체를 할당하기 전에 반환했기 때문에, first() 호출문으로 반환된 값은 undefined
first(function (error, value) {
if(error) {
}
else {
console.log(value);
}
})
- foo? 함수의 인자로 콜백함수 넘겨주고 비동기 처리가 끝난 후 콜백함수를 실행하여 정상적으로 데이터를 가지고 옴
- 콜백함수 호출 시점의 권한이 개발자에게 있는 것이 아니라 제어권을 넘겨받은 코드에게 있음 → 우리는 해당 콜백 함수의 호출을 지켜볼 수밖에 없음
✨콜백함수란?
- 콜백함수는 다른 함수에 매개변수로 넘겨준 함수를 말함
- 함수를 명시적으로 호출하는 것이 아니라 이벤트가 발생했을 때 시스템에 의해 호출
- 콜백은 다른 함수(A)의 전달인자로 넘겨주는 함수(B)를 말함
- 매개변수를 넘겨 받은 함수(A)는 콜백함수 (B)를 필요에 따라 즉시 실행할 수도, 아님 나중에 실행할 수도
- 콜백지옥
- JS를 이용한 비동기 프로그래밍시 발생하는 문제 → 매개변수로 넘겨지는 콜백함수가 반복되어 코드의 들여쓰기 수준이 깊어지는 현상
- 이벤트 처리나 서버통신과 같은 비동기적인 작업을 수행하기 위해 이런 형태가 자주 등장 → 가독성 떨어지고, 코드 수정이 어려워짐
//1
step1(function (value1) {
step2(function (value2) {
step3(function (value3) {
step4(function (value4) {
step5(function (value5) {
step6(function (value6) {
// Do something with value6
});
});
});
});
});
});
//2
getData(function(data) {
processData(data, function(processedData) {
renderData(processedData, function() {
console.log('Done!');
});
});
});
- #1) step1에서 어떤 처리 이후 그 결과를 받아와, 인자로 전달된 익명함수의 매개변수를 넘겨줌. 이후 step2에서 어떤 처리를 하고, 다음 익명함수가 실행 → 피라미드 모양으로 기술
- #2)
- 서버로부터 데이터를 받아옴
- 가져온 데이터를 처리
- 데이터를 화면에 출력
- getData()함수는 서버로부터 데이터를 가져오는 비동기 함수. processData()는 데이터를 처리하는 함수, renderData()는 데이터를 화면에 출력하는 함수 → 각 함수는 콜백함수를 인자로 받아 작업을 수행
- 콜백함수가 중첩되면서 코드 가독성이 떨어지고 유지보수가 어려움
- getData 함수가 실패했을때 어떻게 처리할 지, processData함수에서 오류났을 때 또한 예외처리를 추가 해야함 → 이러면 또 코드가 더러워짐
- 해결책) Promise 객체 사용하기
- Promise 객체를 사용하며 비동기 작업을 순차적으로 처리 → then 메서드를 호출하여 다음 작업을 지정하면, 이전 작업이 완료된 후 다음 작업 실행 ⇒ 콜백지옥에서 벗어남
- 어떤 작업의 중간 상태를 나타내는 오브젝트 → 미래에 어떤 종류의 결과가 반환됨을 promise 해주는 오브젝트
- 비동기 작업이 맞이할 미래의 완료 또는 실패와 결과값을 나타냄
getData() .then(processData) .then(renderData) .then(function() { console.log('Done!'); })
- 비동기 처리에 사용되는 객체 ⇒ 내용은 실행되지만 결과를 아직 반환하지 않은 객체
- Pending(대기) → Fulfilled(이행) → Rejected(실패)
- 완료되지 않았다면 대기, 완료되면 이행, 실패나 오류면 Reject 상태
const condition = true;
const promise = new Promise((resolve, reject) => {
if (condition) {
resolve('resolved');
} else {
reject('rejected');
}
});
promise
.then((res) => {
console.log(res);
})
.catch((error) => {
console.error(error);
});
- 결과)
- conditon 값에 따라 promise의 반환 값이 결정
- 값이 참이면 resolve, 아니면 reject
- resolve한 반환 값에 대해서 then()을 통해 결과 값을 반환할 수도
- reject의 반환 값에 대한 catch()를 통해 반환
- then(), catch() 문의 체이닝을 통해 비동기 로직의 성공 여부에 따른 분기처리 가능
✨async/await
- async, await 적용하기
- callback이나 promise는 꼬리에 꼬리를 무는 코드가 나올 수도. 흔히 콜백지옥, then 지옥
- await을 통해 Promise 반환 값을 받아올 수도
- 하지만 await은 async 함수 안에서만 작동
const variable = await Promise;
//promise 반환값을 받아 variable
- 차이점
- 응답값을 명시적인 변수에 담아 사용하므로 직관적으로 변수를 인식
- 에러 핸들링
- Promise는 .catch 문으로 에러 핸들링 가능, async/await은 에러핸들링이 없어서 try-catch() 문을 활용해야 함
- 코드 가독성
- Promise의 then() 지옥의 가능성 줄여줌
- async/await은 비동기 코드가 동기 코드처럼 읽히게 해줌
- Promise.all()
- Promise.all()은 여러 개의 Promise들을 비동기적으로 실행하여 처리할 수 있도록
- 여러개의 Promise 들 중 하나라도 reject면 반환하거나 하나라도 에러가 나면 바로 reject
const result: any[] = await Promise.all([
fetchNameList(),
fetchFruits(),
fetchTechCompanies(),
]);
// time end
console.timeEnd('promise all example');
console.log('%j', result);
- 전체 실행 시간이 402ms. Promise 들의 처리시간은(300, 200, 400ms) 가장 긴 시간인 400ms와 비슷한 시간 → 비동기 처리임
- 각각 처리 결과를 변수에 담을 수도 있음
const [names, fruits, companies] = await Promise.all([
fetchNameList(),
fetchFruits(),
fetchTechCompanies(),
]);
console.log('%j, %j, %j', names, fruits, companies);
- 동기 처리를 할려면?
const names: string[] = await fetchNameList();
const fruits: string[] = await fetchFruits();
const companies: string[] = await fetchTechCompanies();
// time end
console.timeEnd('promise all example');
console.log('%j, %j, %j', names, fruits, companies);
- 905ms. 300ms, 200ms, 400ms을 모두 더한 값 900ms.
- 서로 영향을 주지 않으면 비동기처리가 훨씬 빠른 속도로 처리됨
- 에러처리
- 하나라도 reject거나 에러면 모두 reject
- 마무리
- 여러개의 Promise들이 비동기적으로, 정상적으로 끝났음을 알 수 있는 방법
- 에러를 던지지 않고 정상적으로 종료되었다는 것은 모든 Promise들을 정상적으로 처리되었다는 증거
끝으로,
⭐ Promise와 Callback를 비교 설명
- 콜백을 사용하면 비동기 로직의 결과값을 처리하기 위해서 콜백 안에서만 처리를 해야하고 콜백 밖에서는 비동기에서 온값을 알 수 없음
- promise를 사용하면 비동기에서 온 값이 promise 객체에 저장되기 때문에 코드 작성에 용이
- 프로미스를 사용하면 프로미스 객체에 비동기가 처리된 결과값이 저장
- 콜백의 경우 매번 비동기를 실행해야지 그 값을 사용할 수 있지만, 프로미스는 .then 메소드를 통해 저장되어 있는 값을 원하는 때에 사용
'자바스크립트의 정석 🟡' 카테고리의 다른 글
[자바스크립트] CommonJS vs ES Modules (1) | 2024.09.10 |
---|---|
[자바스크립트] callback 정리 ✨ (0) | 2024.01.28 |
[자바스크립트] 프로토타입 정리 ✨ (0) | 2024.01.26 |
[자바스크립트] closure 정리 ✨ (0) | 2024.01.24 |
[자바스크립트] this 정리 ✨ (1) | 2024.01.23 |