시작으로
Vue.js
프로젝트 개발을 진행하면서 비동기 처리에 따른 상태관리 및 DOM 처리 때문에 하루종일 애를 먹었다.
해결하고나니 간단한 문제였는데 당시에는 왜?
생각하지 못했는지 생각해봤다.
- 비동기 처리에 대한 이해 부족
- 라이프사이클 훅에 대한 이해 부족
- 디자인 패턴 이해 부족
실무에서 나름 많이 사용했기에 익숙하다고(?) 자신했었지만 응용을 하니 확실히 깨달았다.
조금 안다고 자만하지말자.
그래서, 오늘은 비동기 처리
에 대해 정리하려고 한다.
비동기 처리란 무엇인가?
[Javascript] 자바스크립트의 동작원리 포스팅을 참고하면 비동기 처리에 대한 이해를 도울 수 있다.
간단하게 말하면, 특정 명령을 끝날 때까지 기다리지 않고 다음 코드를 먼저 실행할 수 있도록 하는 자바스크립트의 특성이다.
이러한 자바스크립트의 비동기 처리로 인해 병렬처리
를 한다고 오해하는 사람들이 있다. 절 대
아니므로 위의 포스터를 참고하길 바란다.
비동기 처리의 이해
Ajax 호출
가장 대표적인 비동기 처리는 Ajax
와 setTimeout()
이다.
예제를 통해 어떤 문제가 있는지 살펴보자.
// ajax get 요청을 통해 데이터를 응답받는다. 그 응답받은 데이터를 `res_data`에 담고 return한다.
function asycFn() {
var res_data;
$.get('URL', function(res) {
res_data = res;
});
return res_data;
}
console.log('시작');
console.log(asycFn());
console.log('끝');
위의 코드를 실행해보면
시작
-> 끝
-> undefined
로 출력이 될 것이다.
자바스크립트 동작원리를 간단하게 설명하자면 비동기처리로 호출되는 함수들은 Call Stack
으로 쌓아서 하나하나씩 처리하지 않고
Callback Queue
라는 곳에 보내진다. 그리고 비동기처리가 완료 되었더라도 Call Stack
이 비어있을 때까지 대기했다가 비어 있으면 이벤트 루프를 돌려 콜백함수를 콜스택에 Call Stack
에 넣는다.
음.. 실행순서가 시작
-> 끝
까지는 이해했는데 왜 asycFn()
함수의 값이 undefined
으로 출력되지?
여기서 이상한 것을 감지했다면 당신은 고수
비동기로 호출한 함수는 마지막에 콜백함수
를 Call Stack
에 넣는다. 이게 가장 중요하다.
아까 설명했다시피 $.get()
으로 데이터를 요청하고 받아올 때까지 기다리지 않고 다음 코드로 넘어간다.
여기서 return res_data;
을 실행하기 때문에 res_data
의 값은 undefined
인 것이다.
setTimeout() 호출
자바스크립트의 비동기처리는 Callback Queue
라는 곳에 보내진다고 했다. 이것은 코드 호출 순서를 이해하는데 중요하다.
Web API 중 하나인 setTimeout()
을 예제로 실행해보자.
당연히 지정한 시간만큼 기다렸다가 실행되기 때문에 1
-> 3
-> 2
순서로 출력되는 것을 예상할 수 있다.
console.log('1');
setTimeout(function() {
console.log('2');
}, 1000);
console.log('3');
그렇다면 지정한 시간을 0
초로 하면 어떻게 출력될까?
console.log('1');
setTimeout(function() {
console.log('2');
}, 0);
console.log('3');
위의 실행순서와 마찬가지로 1
-> 3
-> 2
순서로 출력되고 있다.
아까 설명했듯이 자바스크립트의 비동기처리는 처리시간과 관계없이 무조건 Callback Queue
으로 보내진다.
그리고 Call Stack
이 비어있으면 스택에 올려 처리한다.
이러한 자바스크립트의 동작원리를 이해하는 것은 정말 중요하다.
Callback 함수로 비동기처리의 문제 해결하기
앞의 Ajax
의 예제를 보면 서버에서 받아온 데이터로 후속 작업을 하고싶을 때 콜백(callback) 함수를 사용한다.
콜백(callback)
를 사용하면 특정 로직이 끝났을 때 원하는 동작을 실행시킬 수 있다.
function asycFn(callback) {
var res_data;
$.get('URL', function(res) {
callback(res);
});
}
asycFn(function(data){
console.log(data); // // $.get()의 결과값이 data에 전달됨
})