
먼저 비동기 프로그래밍을 알아보기 전에 동기 처리와 비동기 처리에 대해서 알아보자
- 동기적으로 작업이란?
-> 순서가 보장 된 처리 방식, 다른 작업을 완료 전 까지 다른 작업을 블로킹(작업 중단)
하는 처리 방식- 비동기적으로 작업이란?
-> 순서가 보장 되지 않은 처리 방식, 다른 작업이 완료 되기 전에도 블로킹(작업 중단)
하지 않고 다음 작업을 실행하는 처리 방식
간단한 예시로 동기적 처리 방식에 대해 알아보자
function foo () {
console.log("foo");
}
function bar () {
console.log("bar");
}
foo(); // foo
bar(); // bar
위와 같이 어떠한 작업이 끝이 나고 나서 그 다음 작업을 실행 순차적 실행
간단한 예시로 비동기적 처리 방식에 대해 알아보자
// 위와 같이 실행을 하되 setTimeout으로 바꾸어 실행 해보자
function foo () {
console.log("비동기에서 foo");
}
function bar () {
console.log("비동기에서 bar");
}
setTimeout(foo, 0);
bar();
실행을 해보면 비동기에서 bar ,비동기에서 foo 이렇게 나오게 된다.
왜 foo가 먼저 실행이 되지 않고 bar가 먼저 실행이 될까?
setTimeout(foo, 0)가 foo를 비동기적으로 실행 되도록 예약bar는 동기적으로 실행bar 함수가 실행 후 출력bar 함수가 실행이 완료 되었기에 스택(Stack)이 비어진 상태foo 함수가 비어 있는 스택(Stack)으로 이동foo 함수가 실행 후 출력이러한 과정을 거치기에
setTimeout이 0초라고 하더라도 바로 실행 되지 않는 현상이다.
함수를 호출할 때, 해당 함수의 정보(변수, 매개 변수 등) 스택에 푸쉬(push),
함수가 실행이 완료 되면 해당 정보를 스택에서 팝(pop)되어 제거 -> 스택이 비어짐
Escape Callback Hell🔥
연속적으로 발생하는 비동기 함수를 처리할 때, 비동기 함수의 결과를 사용하기 위해 콜백 함수가
중첩되어 복잡해지는 현상을 해결해보자
resolverejecta, b의 값을 더하고 그 값을 콜백함수에 전달해서 X2를 하고, 또 다시 -X2를 하는 함수를 콜백함수로 나타냈다.
function taskA(a, b, cb) {
setTimeout(() => {
const res = a + b;
cb(res);
}, 1000);
}
function taskB(a, cb) {
setTimeout(() => {
const res = a * 2;
cb(res);
}, 1000);
}
function taskC(a, cb) {
setTimeout(() => {
const res = a * -2;
cb(res);
}, 1000);
}
taskA(4, 4, (a_res) => {
console.log("taskA : ", a_res); // taskA : 8
taskB(a_res, (b_res) => {
console.log("taskB : ", b_res); // taskB : 16
taskC(b_res, (c_res) => {
console.log("taskC : ", c_res); // taskC : -32
});
});
});
흠 보기만 해도 조금 복잡하기도 하고 이게 줄이 더 길어지면 더 복잡해 질 것이다.
위의 코드를 promise를 통해 코드를 고쳐보자
new Promise((resolve, reject) => {})
- 비동기 작업이 성공 했을 때
(...resolve)호출- 비동기 작업이 실패 했을 때
(...reject)호출
그럼 아까 코드를 수정 해보자
function taskA(a, b) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const res = a + b;
resolve(res);
}, 1000);
});
}
function taskB(a) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const res = a * 2;
resolve(res);
}, 1000);
});
}
function taskC(a) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const res = a * -2;
resolve(res);
}, 1000);
});
}
taskA(4, 4)
.then((a_res) => {
console.log("taskA : ", a_res);
return taskB(a_res);
})
.then((b_res) => {
console.log("taskB : ", b_res);
return taskC(b_res);
})
.then((c_res) => {
console.log("taskC : ", c_res);
});
promise를 통해 코드를 개선하였는데 어떠한 점이 이점인지에 대해 알아보자
promise를 사용함으로 콜백 함수를 직접 전달하지 않아도 된다는 점then체이닝의 사용 : then을 사용함으로 코드의 가독성이 올라갔다는 점const 코드끊기 = taskA(4, 4).then((a_res) => {
console.log("taskA : ", a_res);
return taskB(a_res);
});
console.log("a")
코드끊기
.then((b_res) => {
console.log("taskB : ", b_res);
return taskC(b_res);
})
.then((c_res) => {
console.log("taskC : ", c_res);
});
또한 콜백함수와 달리 중간에 코드를 나눠 어떠한 작업을 추가 할 수 있다는 점이 있다.
그렇지만 아직 복잡한 경향이 있다 그렇기에 이번에는 async, await를 통해 코드를 개선해보자
Escape CallbackHell🔥, Escape thenHell🔥
async function makeAsync() {
return "Make Async";
}
console.log(makeAsync()); // Promise {<pending>}
async를 붙인 함수makeAsync의 결과값이 promise로 나오는 것을 볼 수 있다
-> then을 사용 가능한 비동기 처리 함수가 된다
async function makeAsync() {
return "Make Async";
}
makeAsync().then((res) => {
console.log(res); // Make Async
});
return한 값인 "Make Async"가 resolve값으로 쓰이는 것을 알 수 있다.
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function makeAsync() {
await delay(3000);
return "Make Async";
}
makeAsync().then((res) => {
console.log(res); // 3초 뒤Make Async
});
await 비동기 함수 앞에 붙이게 되면 비동기 함수를 마치 동기 함수로 사용 할 수 있게 된다.await 함수가 끝나고 나서 뒤에 함수가 실행이 된다.await 함수는 async가 붙은 함수 내에서만 사용 가능하다.function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function taskA(a, b) {
await delay(1000);
const res = a + b;
return res;
}
async function taskB(a) {
await delay(1000);
const res = a * 2;
return res;
}
async function taskC(a) {
await delay(1000);
const res = a * -2;
return res;
}
async function main() {
try {
const a_res = await taskA(4, 4);
console.log("taskA : ", a_res);
const b_res = await taskB(a_res);
console.log("taskB : ", b_res);
const c_res = await taskC(b_res);
console.log("taskC : ", c_res);
} catch (error) {
console.log("Error : ", error);
}
}
main();
async, await를 사용하여 코드를 수정 했는데 확실히 어떠한 작업인지 구분이 간다await을 통하여 비동기 처리 방식 내에서 그 결과를 기다리는 방식으로 작동이 된다.main함수를 통하여 작업이 순서대로 수행되고, 에러핸들링이 된다.