동기와 비동기는 서로 상반되는 말인데요. 우선, 동기는 A의 일이 끝나야만 B의 일이 끝나고 C의 일을 시작하는... 의 식으로 앞의 일이 끝나야만 뒤의 일을 진행할 수 있는 처리 방식을 말합니다. 비동기는 반대로 동시의 여러 함수를 호출하거나 하나의 일이 끝나기 전에 다른 함수를 호출할 수도 있습니다.
비동기로 처리를 하게 되면 동시에 여러 일을 할 수 있으므로 요즘에 많이 사용되는 기법입니다. 특히 자바스크립트는 단일 쓰레드 방식이라서 원래 동시에 여러 일을 할 수 없기 때문에 비동기 방식을 많이 사용합니다.
동시에 일을 처리한다는 것은 좋은데, 비동기 속에서도 동기적으로 처리되어야 하는 일들은 어떻게 해야할까요? 예를 들면 A에서 연산한 결과값을 가지고 B에서 사용해야 한다면요?
아주 전통적인 방법으로 콜백함수가 있습니다. 쉬운 방법이지만 비동기 코드가 조금만 많아지면 코드 가독성이 많이 떨어집니다. 아래는 숫자 n을 파라미터로 받아와서 1초마다 1씩 더해서 출력해야 하는 예제입니다.
function increaseAndPrint(n, callback) {
setTimeout(() => {
const increased = n + 1;
console.log(increased);
if (callback) {
callback(increased);
}
}, 1000);
}
increaseAndPrint(0, n => {
increaseAndPrint(n, n => {
increaseAndPrint(n, n => {
increaseAndPrint(n, n => {
increaseAndPrint(n, n => {
console.log('끝!');
});
});
});
});
});
어떤가요? 계속 옆으로 파고 들어가고 이해하기가 쉽지 않죠? 코드가 명료하게 보이지 않습니다.
또한 콜백처리는 try catch로 에러를 잡아낼 수도 없습니다.
promise는 체이닝 방식으로 콜백지옥을 해결해줍니다. promise를 리턴한다면 .then 메소드를 통해 이어서 작업할 수 있습니다. 또한 성공, 에러 처리가 가능합니다.
먼저 생성자 함수를 통해 promise를 만들어 성공 시, 실패 시 처리를 설정합니다.
function increaseAndPrint(n) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const value = n + 1;
if (value === 5) {
const error = new Error();
error.name = 'ValueIsFiveError';
reject(error);
return;
}
console.log(value);
resolve(value);
}, 1000);
});
}
increaseAndPrint(0)
.then(n => {
return increaseAndPrint(n);
})
.then(n => {
return increaseAndPrint(n);
})
.then(n => {
return increaseAndPrint(n);
})
.then(n => {
return increaseAndPrint(n);
})
.then(n => {
return increaseAndPrint(n);
})
.catch(e => {
console.error(e);
});
이 외에도 promise.all, promise.race 등이 있습니다.
const results = Promise.all([getDog(), getRabbit(), getTurtle()]);
const first = Promise.race([getDog(), getRabbit(), getTurtle()]);
async-await는 promise를 좀 더 쉽게 사용하고, 절차지향적 코드와 잘 맞습니다.
promise가 있는 함수에 async를 붙이고, 해당 promise함수에 await를 붙여 사용하면 동기로 사용이 됩니다. 또한 async를 사용한 함수는 자동으로 promise를 리턴하게 되어있어 체이닝 사용이 가능합니다.
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const getDog = async () => {
await sleep(1000);
return '멍멍이';
};
const getRabbit = async () => {
await sleep(500);
return '토끼';
};
const getTurtle = async () => {
await sleep(3000);
return '거북이';
};
async function process() {
const dog = await getDog();
console.log(dog);
const rabbit = await getRabbit();
console.log(rabbit);
const turtle = await getTurtle();
console.log(turtle);
}
process();