자바스크립트를 처음 배우면서 가장 당황했던 것은, 코드가 반드시 순차적으로 실행되지 않는다는 것이었다.
입력을 받아 입력값을 바탕으로 간단한 내용을 처리하는 코드를 짜면서도, 입력을 받기도 전에 내용을 처리하려 들어서 고작 20줄 정도를 짜면서도 상당히 애를 먹고서야, 이 문제가 JS의 비동기적 특성 때문인 것을 깨달았다.
그래서 비동기 처리 방법에 대해 간단히 짚고 넘어가기로 했다.
비동기 처리란 무엇인가?
비동기에 대해 이해하기 위해서는 우선 다른 프로그램들의 동작 방식인 동기에 대해 확실히 알고 있는 게 좋다.
동기(sync)란, 요청한 것에 대한 결과가 반환되기까지 그 자리에서 기다리는 처리 방식이다.
비동기(Async)란, 요청만 하고 결과 반환 여부와 상관 없이 다음 동작을 실행하러 움직이는 처리 방식이다.
JS에서는 이러한 비동기적 특성 때문에 특정 요청에 대한 결과가 반영되기 전에 해당 요청을 사용하는 동작이 실행되어 코드가 예상한 대로 동작하지 않는 경우가 발생한다.
이를 해결하기 위해서는 세 가지의 해결법이 존재한다.
- 콜백(callback) 함수 사용
- 프로미스(promise) 객체 사용
- async/await 사용
세 가지 해결법은 등장 순으로 나열하였다. 즉 1번을 보완한 2번이 등장한 뒤, 1번과 2번을 보완한 3번이 등장했다고 볼 수 있겠다.
콜백(callback) 함수
콜백 함수란, 함수 내에서 파라미터로 주고 받는 함수를 말한다.
사용법은 아래 예시와 같다.
console.log("HELLO 1")
// 콜백 사용법 //
function printHello(callbackFunc){
//여기서 printHello 인자에 값을 넘겨줄 때까지 9줄 함수가 시작되지 않음
setTimeout(function(){callbackFunc("HELLO CALLBACK")}, 3000);
}
printHello(function(result){
console.log(result);
// 여기 있으면 "HELLO CALLBACK"이 먼저 출력
// console.log("HELLO 2")
});
// 여기 있으면 "HELLO 2"가 먼저 출력
// console.log("HELLO 2")
여기서 콜백 함수는 이 부분이다.
function(result){
console.log(result);
// 여기 있으면 "HELLO CALLBACK"이 먼저 출력
// console.log("HELLO 2")
}
이 함수가 callbackFunc로 넘겨지고, 해당 함수를 호출한 함수에서 전달한 값은 매개인자로 넘긴 result에 담기게 된다. 그 때에서야 이 함수 내 문장이 실행된다.
console.log("HELLO 2") 의 주석 처리를 하나씩 제거해 실행해봄으로써 콜백 함수의 동작을 이해할 수 있을 것이다.
그러나 이 콜백이 중첩되어 쌓이면 콜백 지옥이라는 사태를 불러올 수 있다.
이러한 상황을 예방하기 위해 Promise, async와 await 등을 사용한다.
프로미스(Promise)
Promise라는 객체를 생성하고, 생성자로 resolve(성공 시), reject(실패 시) 값을 전달하는 매개 인자를 선언한다.
console.log("HELLO 1");
function printHello(){
return new Promise(function(resolve, reject){
let call = "CALLBACK";
resolve("HELLO " + call);
// or reject("error");
})
}
printHello().then(function(resolvedData){
console.log(resolvedData);
// console.log("HELLO 2");
}).catch(function(error){
console.log(error);
})
// console.log("HELLO 2");
성공 시의 인자는 .then을 통해, 실패 시의 인자는 .catch를 통해 사용할 수 있다.
async와 await
가독성을 위해 새로 등장한 문법이다.
그냥 비동기 처리할 메소드에 async를, Promise를 반환하는 메소드에 await를 붙여주면 된다.
console.log("HELLO 1");
function printHello(){
return new Promise(function(resolve, reject){
let call = "CALLBACK";
resolve("HELLO " + call);
// or reject("error");
});
}
async function printResult() {
// await로 선언된 함수는 반드시 Promise 객체를 반환
let result = await printHello();
console.log(result);
// console.log("HELLO 2");
}
// console.log("HELLO 2");
printResult();
항상 순차적으로 처리되는 코드만 보다가 이런 코드를 보니 신기하기도 하고, 복잡하기도 하다.
JS에 대해서는 더 기본적인 공부가 필요할 것 같다.