동시에 일어난다는 의미. 요청을 보낸 후 해당 요청의 응답을 받아야 다음 동작을 실행하는 방식
동시에 일어나지 않는다는 의미. 요청을 보낸 후 응답과 관계없이 다음 동작을 실행하는 방식
당신이 카페에서 커피를 주문하려고 할 때, 점원이 한 명 뿐이라면 당신을 포함한 손님들은 점원이 맨 앞 손님의 주문을 받고, 음료를 만들고, 계산하는 것까지 기다려야한다. 마치 Queue처럼 선입선출 과정의 코드 처리 순서를 동기 방식
이라고 부른다.
여기서 여러 명의 점원이 있다고 생각해보자. 이 점원들은 주문을 받은 순서대로 일을 처리하는 것이 아니라 각자 파트대로 음료를 나누어 먼저 완성되는 쪽이 음료를 내놓는다. A, B 순서로 주문을 했더라도 B의 음료가 먼저 완성되면 주문한 순서와 관계없이 B가 먼저 음료를 가져갈 수 있는 것이다. 여기서 점원들은 비동기 방식
으로 수행했다고 할 수 있다.
✔️ 동기 방식
장점 : 설계 매우 간단. 직관적
단점: 결과가 주어질때까지 아무것도 하지 못하고 대기
✔️ 비동기 방식
장점: 결과가 주어지는데 시간이 오래 걸리더라도 그 시간 동안 다른 작업을 할 수 있음. 자원을 효율적으로 사용할 수 있음
단점: 동기보다 설계 복잡
→ setTimeout
은 JavaScript의 전형적인 비동기 작업이다.
function f1(){
setTimeout(() => {
console.log(1);
}, 2000); // watiting for 2s
}
function f2() {
setTimeout(() => {
console.log(2);
}, 1000); // waiting for 1s
}
f1();
f2();
// result: 2
// 1
f1()
은 2초 뒤에 1이 출력되고, f2()
는 1초 뒤에 2가 출력된다. f1()
이 먼저 호출됐기 때문에 1 2
라는 결과를예상할 수도 있지만, JavaScript는 비동기 방식으로 처리되기 때문에 2 1
이 출력될 것이다.
그렇다면, 이를 순차적으로 출력하려면 어떻게 해야 할까?
setTimeout(() => { // f1()
console.log(1);
setTimeout(() => { // f2()
console.log(2);
setTimeout(() => {
console.log(3);
//...
}, 1000)
}, 1000)
}, 1000)
// result: 1
// 2
// 3
순차적으로 출력되지만, Callback Hell의 형태처럼 코드가 점점 길어진다.
// Callback Hell 예시
// Callback Hell 은 비동기 프로그래밍시 발생하는 문제이다.
function Callback(callback) {
function Callback2(callback2) {
function Callback3(callback3) {
//...
}
}
}
JavaScript는 비동기 처리를 위하여 하나의 패턴으로 콜백 함수를 사용한다.
하지만 전통적인 콜백 패턴은 Callback Hell로 인해 가독성이 나쁘고 비동기 처리 중 발생한 에러의 처리가 곤란하다.
→ Promise
는 Callback Hell을 보완하며 비동기 처리 시점을 명확하게 표현한다. 주로 서버에서 받아온 데이터를 표시할 때 사용한다.
new Promise((resolve, reject) => {
// ...
})
.then(() => {
// ...
})
.catch(() => {
// ...
})
Promise
내부에 익명 함수를 호출할 수 있으며, 인자는 resolve
와 reject
를 받는다. Promise
는 체이닝 기법을 통해 then
과 catch
를 호출할 수 있다.
익명 함수는 무조건 실행되며, 내부 작업이 성공적으로 처리 되었을 때 resolve
를 호출하여 then
으로 분기시킬 수 있다. 만약 처리가 비정상적으로 되었다면 reject
를 호출하여 catch
로 분기해 에러를 출력하는 쪽으로 빠질 수 있다.
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('멍');
}, 1000);
})
.then(result => {
console.log(result + '!');
return result + result;
})
.then(result => {
console.log(result + '!');
return result + result;
})
.then(result => {
console.log(result + '!');
});
// result: 멍!
// 멍멍!
// 멍멍멍
이 1초에 한 줄씩 출력된다.
ES8에서는 async
await
를 지원한다. Promise
로 제공하던 함수들을 더 간결하고 직관적으로 실행할 수 있다.
async function getInfo() {
let chiwawa = await db.collection('강아지').doc('몰키').get();
console.log(chiwawa.data()); // await 다음에 실행됨
위 코드는 firestore
에서 강아지 정보를 가져오는 비동기 함수 예시이다. function
앞에 async
가 쓰여진 함수가 실행 중에 await
라는 코드를 만나면 코드의 진행이 멈추고 await
코드가 끝난 뒤에 그 다음 작업을 실행하게 된다.await
를 기다렸다가 정보를 받은 후 아래의 console.log
코드를 실행하게 되는 것이다. 만약 await
를 붙이지 않는다면 데이터를 받아오기 전에 console.log
넘어가고, 콘솔에는 undefined
가 출력될 것이다.