동기적 /비동기적 무슨 말일까?
동기적인 것의 의미는 어떠한 함수가 실행되고 모든 기능을 마친 후 다음 함수가 실행되는 것을 말합니다.
비동기적은 한 함수가 실행될 때 다른 함수들도 같이 실행될 수 있는 것을 말합니다.
비동기 함수를 동기적 으로 제어하기
비동기적인 함수도 동기적 함수처럼 Callback
,Promise
, Async/Await
를 통해 순서를 제어할 수 있습니다.
먼저 Callback
함수를 통해 순서를 제어할 수 있습니다.
const printString = (string) => {
setTimeout(function () {
console.log(string);
}, Math.floor(Math.random() * 100) + 1);
};
const printAll = () => {
printString('A');
printString('B');
printString('C');
};
//printAll()함수를 호출해보면 a,b,c 의 순서가 랜덤으로 호출됩니다.
아래와 같이 콜백함수로 제어해준다면
const printString = (string, callback) => {
setTimeout(function () {
console.log(string);
callback();
}, Math.floor(Math.random() * 100) + 1);
};
const printAll = () => {
printString('A', () => {
printString('B', () => {
printString('C', () => {});
});
});
};
// console
A
B
C
위와 같이 적은 갯수의 함수로 이루어져있을땐 괜찮을 수 있지만 수백개라면?
Callback Hell 발생 !! => 가독성 저하의 단점
const printAll = () => {
printString('A', () => {
printString('B', () => {
printString('C', () => {
printString('D', () => {
printString('E', () => {
printString('F', () => {
printString('G', () => {
printString('H', () => {
printString('I', () => {
printString('J', () => {
printString('K', () => {
printString('L', () => {
printString('M', () => {
});
});
});
});
});
});
});
});
});
});
});
});
};
Promise
Promise
Callback Hell을 방지하며 비동기 코드를 제어할 수 있습니다.
- Promise는 class 이기 때문에 new 키워드 를 통해 객체를 생성해야한다.
- Promise도 비동기 처리를 위한 콜백함수를 인자로 받는다.resolve, reject 함수
resolve : 정상처리 되었을 경우 호출
reject : 에러가 발생했을 경우 호출
const printString = (string) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(value : console.log(string););
reject(error : 에러 메세지);
}, Math.floor(Math.random() * 100) + 1);
});
};
Promise 객체는 state, result 를 내부 프로퍼티를 갖는데, 이는 메서드를 통해 접근이 가능합니다.
state
(상태)
- 기본 상태 - pending(대기)
- 콜백함수가 작동 성공하면 fulfilled(이행) 상태
- 에러가 발생했다면 reject(거부) 상태
result
(결과)
- 기본 상태 - undifined
- 콜백함수 작동이 성공하면 resolve(value)가 호출되며 값 = value
- 에러가 발생해 실패하면 reject(error)가 호출되며 값 = error
.then.then
은 리턴 값을 다음 .then
콜백 함수의 인자로 받아올 수 있다.
.catch.catch
는 에러가 발생했을 경우 reject
함수를 호출하고, .catch
로 접근할 수 있다.
.finallyfinally
는 코드의 성공/실패 여부에 관계없이 모두 접근할 수 있다.
Promise chaining은 다음과 같이 비동기 작업들을 동기적으로 진행해야 할 때 쓰입니다.
let promise = new Promise((resolve, reject) => {
resolve("성공");
});
promise
.then(value => {
console.log(value);
return 'A성공';
})
.then(value => {
console.log(value);
return 'B성공';
})
.catch(error => {
console.log(error);
return '에러';
})
.finally(() => {
console.log("성공이든 실패든 작동!");
// "성공이든 실패든 작동!"
})
//return 처리를 해주지 않으면 callback hell 같은 현상이 발생할 수 있다.
//promise가 성공했을 경우 resolve 가 첫번째 .then 으로 전달되어 A성공을 리턴하고
// 첫번째 .then 이 성공하여 두번째 .then 으로 전달 되어 B성공을 리턴합니다.
//앞의 코드에서 error 가 발생했다면 에러를 리턴하게되고
//성공이든 실패든 맨 마지막엔 .finally 의 인자로 전달되어 "성공이든 샐패든 작동!"이 작동됩니다.
Promise.all() 은 비동기 작업들을 한번에 처리하고 싶을 때 사용.
- Promise.all 의 인자는 배열로 받을 수 있다.
- 배열로 받은 Promise 중 하나라도 에러가 발생하면 즉시 종료되며 .catch(err) 가 작동되며 err 를 리턴한다.
예시
const promiseOne = () => new Promise((resolve, reject) => setTimeout(() => resolve('1초'), 1000));
const promiseTwo = () => new Promise((resolve, reject) => setTimeout(() => resolve('2초'), 2000));
const promiseThree = () => new Promise((resolve, reject) => setTimeout(() => resolve('3초'), 3000));
// 기존의 Promise chaining을 사용 = 총 6초 걸림
const result = [];
promiseOne()
.then(value => {
result.push(value);
return promiseTwo();
})
.then(value => {
result.push(value);
return req3();
})
.then(value => {
result.push(value);
console.log(result);
// ['1초', '2초', '3초']
})
// Promise.all 사용 = 3초
Promise.all([promiseOne(), promiseTwo(), promiseThree()])
.then((value) => console.log(value))
// ['1초', '2초', '3초']
.catch((err) => console.log(err));
Promise Hell !!
Promise chaning 를 통해 비동기 코드를 제어할 수 있지만, 코드가 길어질 수록 가독성이 낮아지는 Promise Hell 이 발생할 수 있다.
const printAll = () => {
printString('A').then((value) => {
console.log(value);
printString('B').then((value) => {
console.log(value);
printString('C').then((value) => {
console.log(value);
printString('D').then((value) => {
console.log(value);
printString('E').then((value) => {
console.log(value);
printString('F').then((value) => {
console.log(value);
printString('G').then((value) => {
console.log(value);
...
});
});
});
});
});
});
});
};
Async/Await
Async/Await
복잡한 Promise 코드를 간결하게 작성할 수 있다. ES8 에서 async/await키워드를 제공
// 함수 선언식
async function funcDeclarations() {
await 작성하고자 하는 코드
...
}
// 함수 표현식
const funcExpression = async function () {
await 작성하고자 하는 코드
...
}
// 화살표 함수
const ArrowFunc = async () => {
await 작성하고자 하는 코드
...
}
사용법
함수 앞에 async 키워드를 사용하고 async 함수 내에서 await 키워드를 사용
예시
설명 : Promise 가 성공적으로 작동해 console.log(string)이 출력되면 printAll 이 작동되며 await 가 하나씩 출력된다.
const printString = (string) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
console.log(string);
}, Math.floor(Math.random() * 100) + 1);
});
};
const printAll = async () => {
await printString('A');
await printString('B');
await printString('C');
};
printAll();
console.log(
`Async/Await을 통해 Promise를 간결한 코드로 작성할 수 있게 되었습니다.`
);
'프론트엔드 개발 > Javascript' 카테고리의 다른 글
javascript fetch API 란? (0) | 2022.11.27 |
---|---|
Javascript Node.js 모듈 사용법 (0) | 2022.11.23 |
Javascript 타이머 API (0) | 2022.11.22 |
function 대신 class?? class 사용법과 활용 (0) | 2022.11.21 |
객체 지향 프로그래밍 - 프로토타입 체인 (0) | 2022.11.18 |