Js 언어상의 특징으로 싱글 쓰레드를 사용 => 한번에 한가지 일만 처리 가능
이러한 점에 대한 단점들을 보완하기 위해 언어 자체는 병렬 처리를 못하지만,
Js를 읽고 실행하는 엔진에서 병렬처리를 하는 구조
비동기란?
A, B, C 실행시킬 때
동기: A 끝날 때까지 기다리고 B 실행, B 끝날 때까지 기다리고 C 실행
비동기: 그냥 각자 A,B,C 동시 실행
비유하자면 동기: 릴레이 계주 , 비동기 : 100m 달리기
이벤트 기반 동작을 코드로 구현한 방법
1. Callback : 전통적인 Javascript의 이벤트 기반 코딩 방식
2. Promise : Callback의 단점(= Callback 지옥 등...) 보완한 방식
3. Async - Await : Promise의 단점 보완한 방식
1. Callback
function countDown(count, callback) {
console.log(count);
//count ==0 이면 앞서 받아놨던 callback 실행하고(BOOM! 출력) return
if (count === 0) {
callback();
return;
}
//count 줄여나가며 재귀
setTimeout(() => {
countDown(count - 1, callback);
}, 1000);
}
//count == 5, callback 메소드 내용 == BOOM! 출력
countDown(5, ()=>{
console.log("BOOM!");
});
Callback 지옥
function add3(a, callback) {
setTimeout(() => callback(a + 3), 100)
// 100ms가 지난 후 함수로 입력받은 callback에 a + 10값을 다시 입력하여 callback함수 호출
}
add3(10, res => {
console.log(res) // 13
add3(res, res => {
console.log(res) // 16
add3(res, res => {
console.log(res) //19
add3(res, res => {
console.log(res) //22
})
})
})
})
함수 재귀적으로 호출하기에 가독성 떨어짐
2. Promise
promise의 기본 컨셉은 현재는 당장 없을 수 없지만, (보통 데이터를 얻는 데까지 지연이 발생하는 경우)
근미래에 얻을 수 있는 데이터에 접근하기 위한 방법.
promise 의 필요성: 지연이 발생한 경우는 주로 I/O 나 Network를 통해서 데이터를 얻는 과정에서 발생하는데 이는 cpu 입장에서 매우 긴 시간, 앞서 말했듯이 js 는 싱글 쓰레드이기에 이를 기다려 줄 수 없음 (Non-blocking 코드 지향)
Promise는 resolve, reject 라는 2개의 함수형 파라미터를 가짐
//Promise 객체 생성하여 변수에 할당
const promise = new Promise(function(resolve, reject) { ... } );
//보통은 어떤 함수의 리턴 값으로 바로 사용된는 경우 많음
function returnPromise() {
return new Promise((resolve, reject) => { ... } );
}
resolve() 는 미래 시점에 얻게 될 결과를 넘겨주고
reject() 는 미래 시점에 발생할 예외를 넘겨줌
예시 - 직접 Promise 객체 생성해서 사용하는 경우
function devide(numA, numB) {
return new Promise((resolve, reject) => {
if (numB === 0) reject(new Error("Unable to devide by 0."));
else resolve(numA / numB);
});
}
devide(8, 2)
.then((result) => console.log("성공:", result))
.catch((error) => console.log("실패:", error));
// 실행 결과=> 성공: 4
// resolve(numA / numB) 를 타고 .then((result) => 를 타고 출력된 결과
devide(8, 0)
.then((result) => console.log("성공:", result))
.catch((error) => console.log("실패:", error));
// 실행 결과=> 실패: Error: Unable to devide by 0.
// if 문의 numB===0에 걸려 (예외) reject 타고 .catch를 타고 출력된 결과
예시2 - 어떤 라이브러리의 함수 호출시 리턴 받는 Promise 객체 사용하는 경우
// 어떤 서비스의 API 호출시 응답 결과에 따라 결과/예외 처리
// REST API 호출 시 사용되는 fetch()는 URL 인자로 받고 호출 결과를 Promise 객체로 리턴
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then((response) => console.log("response:", response))
.catch((error) => console.log("error:", error));
// resolve된 경우: response: Response { 결과 ~~~ }
// reject된 경우: error: TypeError: Failed to execute 'fetch' on 'Window':~~~
메서드 체이닝(Method Chaining)
then(), catch() 메서드는 또 다른 Promise 객체를 리턴하고,
이 객체는 인자로 넘긴 콜백 함수의 리턴값을 다시 then()과 catch() 를 통해 접근할 수 있게 해줌
예를 들어, fetch()에서 단순히 응답 결과만이 아니라
응답 전문을 json 형태로 출력하고 싶은 경우에는 then() 메서드를 추가로 연결해주면 된다.
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then((response) => response.json())
.then((post) => console.log("post:", post))
.catch((error) => console.log("error:", error));
Promise의 문제점
동일한 이름의 메서드인 then()을 연쇄적으로 호출하고 있어서 도대체 몇 번째 then()에서 문제가 발생한 건지 Stack Trace을 봐도 알기 어려울 수 있음
3. Async - Await
비동기 코드를 동기 코드처럼 작성할 수 있음
async 키워드는 함수, function 앞에 붙는다.
await 키워드는 함수 내부에서만 사용할 수 있고, 비동기 함수가 리턴하는 Promise에서 결과값을 추출한다.
그 말은 바로 await 키워드를 사용하면 바로 다음 라인으로 넘어가는 것이 아닌, 말 그대로 await! 기다려! 준다. (결과값을 얻을 때까지)
function adder_promise(a, b) {
return new Promise((resolve, reject) => {
resolve(a+b);
});
}
function main_promise(a, b, c, d) {
Promise.all([
adder_promise(a, b),
adder_promise(c, d),
])
.then(([r1, r2]) => {
return adder_promise(r1, r2);
})
.then((r3) => {
console.log(`${a}+${b}+${c}+${d}=${r3}`);
});
}
/* 1. main 을 async 함수로 선언 */
async function main(a, b, c, d) {
const [r1, r2] = await Promise.all([
adder_promise(a, b),
adder_promise(c, d),
]);/* 2. 두 promise 함수를 동시에 실행하여 결과를 r1, r2에 저장 */
const r3 = await adder_promise(r1, r2);/*3. r1 과 r2 를 한번 더 adder_promise 로 실행 */
console.log(`${a}+${b}+${c}+${d}=${r3}`);
}
main(1,2,3,4);
// 실행 결과: 1+2+3+4=10
주의할 점: async 키워드가 붙어 있는 함수를 호출하면 명시적으로 Promise 객체를 생성해서 리턴하지 않아도 Promise 객체가 리턴된다.
참고한 글
[자바스크립트] 비동기 처리 2부 - Promise | Engineering Blog by Dale Seo
[자바스크립트] 비동기 처리 3부 - async/await | Engineering Blog by Dale Seo
'미가공 필기(JS)' 카테고리의 다른 글
[JS] Lexical Scope, Dynamic Scope (0) | 2022.08.08 |
---|---|
Nest JS 공식문서 핥기(1) (0) | 2022.05.19 |
댓글