자바스크립트를 공부하면서
비동기에 대해 가장 이해하기 어려웠다.
동기와비동기를 간단하게 말하면
동기: 요청의 그 결과가 동시에 일어난다.비동기: 요청과 결과가 동시에 일어나지 않는다.
로 이야기 할 수 있다. 실제로 나도 이렇게만 이해하고 넘어갔는데 api를 연결하는 작업을 하면서 이것을 명확하게 정리하지 않고 코드를 작성하면 작성자조차 이해하지 못하는 얼레벌레(?)코드가 만들어지는 것을 경험하고 짚고 넘어가고자 정리를 하게 되었다.
웹 서비스를 만들다 보면 처리할 때 시간이 걸리는 작업이 있다.
웹 애플리케이션에서 서버 쪽 데이터가 필요할 때는Ajax기법을 사용하여서버의 API를 호출함으로써 데이터를 수신한다. 이때서버의 API를 사용해야 할 때는 네트워크 송수신 과정에서 시간이 걸리기 때문에 작업이 즉시 처리되는 것이 아닌, 응답을 받을 때까지 기다렸다가 전달받은 응답 데이터를 처리한다.
++Ajax에 관한 이야기는 추후에 DEEP하게 다뤄보려고 한다. 궁금하다면 Getting Started Ajax를 한 번 읽어보길 바란다.
➡️ 이 과정에서 해당 작업을 비동기적으로 처리하게 되는 것이다!
만약 작업을
동기적으로 처리한다면 요청이 끝날 때 까지 기다리는 동안 모든 작업이 올스탑! 하기 때문에 다른 작업을 할 수 없게 된다.
하지만 이를비동기적으로 처리하게 된다면 웹 애플리케이션은 멈추지 않기에 동시에 여러 가지 요청을 처리할 수도 있고, 기다리는 과정에서 다른 함수의 호출도 가능해진다!
서버 API를 호출할 때 이외에도 작업을 비동기적으로 처리할 때가 있는데 바로
setTimeout함수를 사용하여 특정작업을 예약할 때이다.
setTimeout이 실행되는 시점에서 코드가 3초동안 멈추는 것이 아닌, 일단 코드가 위부터 아래까지 다 호출되고 3초 뒤에 지정해준printMe함수를 호출하는 것이다. (결과값이대기중..임당이 출력되고 그 후에Hello World가 출력된다.)
이것이 자바스크립트에서 비동기 작업을 할 때 가장 흔히 사용하는 콜백함수를 이용하는 것이다. 위 코드에서printMe가 3초 뒤에 호출되도록printMe함수 자체를setTimeout인자로 전달해주었는데 이 함수를 콜백함수라 부른다.
예를 들어 파라미터 값이 주어지면 1초 뒤에 10을 더해서 반환하는 함수를 만든다고 하자.
이것이 순차적으로 10,20,30이 되도록 만든다면 콜백함수를 중첩해서 구현해야 한다.
만약 10부터 100까지 증가하는 콜백함수를 구현하게 된다면 중접된 코드로 가독성이 떨어지는 '콜백지옥'에 갇히게 될 것이다. 웬만하면 이러한 형태는 지양하여야 한다. 이러한 콜백 지옥을 해결하기 위해 ES6버전에서는Promise가 나타났다.
function increase(number) {
const promise = new Promise((resolve, reject) => {
//resolve는 성공, reject는 실패
setTimeout(() => {
const result = number + 10;
if (result > 50) {
//50보다 숫자가 크면 에러 발생시키기
const e = new Error("NumberTooBig");
return reject(e);
}
resolve(result);
}, 1000);
});
return promise;
}
increase(0)
.then((number) => {
//Promise에서 resolve된 값은 .then을 통해 받아 올 수 있다.
console.log(number);
return increase(number); //Promise를 리턴하면
})
.then((number) => {
//또 .then으로 처리할 수 있다.
console.log(number);
return increase(number);
})
.then((number) => {
console.log(number);
return increase(number);
})
.then((number) => {
console.log(number);
return increase(number);
})
.catch((e) => {
//도중에 에러는 .catch를 통해 알 수 있다.
console.log(e);
});
이렇게 하면 여러작업을 연달아 처리하게 되어도 함수 안에 함수를 감싸는 중첩의 형태로 처리하지 않고
.then을 통해 그다음 작업을 설정하기에 콜백 지옥보다는 코드를 가독성 있게 만들 수 있다.
하지만
Promise도.then이 계속 꼬리를 무는 모습을 볼 수 있는데 이 또한 코드의 가독성에 방해가 된다. 이러한 단점 해결과Promise를 더욱 쉽게 사용하기 위해 ES8에서async/await문법이 나오게 되었다!
이것은 함수 앞부분에
async키워드를 추가하고 함수 내부에서Promise의 앞부분에await키워드를 사용하면 된다!
이렇게 하면Promise가 끝날 때까지 기다리고, 결과 값을 특정 변수에 담을 수 있다.
function increase(number) {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const result = number + 10;
if (number > 50) {
const e = new Error("숫자가 너무 커요");
return reject;
}
resolve(result);
}, 1000);
});
return promise;
}
async function runTask() {
try {
// try/catch 구문을 사용하여 에러를 처리한다.
let result = await increase(0);
console.log(result);
result = await increase(result);
console.log(result);
result = await increase(result);
console.log(result);
result = await increase(result);
console.log(result);
result = await increase(result);
console.log(result);
result = await increase(result);
console.log(result);
} catch (e) {
console.log(e);
}
}
async-await를 api 호출할 때 습관적으로 사용하고 있었는데 그 원리에 대해서는 잘 이해를 하지 못했던 것 같아요! 명지님 글 덕분에 제대로 정리가 된 것 같아요:) 좋은 글 잘 보고 갑니다!