Notice
Recent Posts
Recent Comments
Link
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Archives
Today
Total
관리 메뉴

도도의 IT이야기

콜백의 개념. 비동기적과 동기적 콜백의 차이점 본문

IT/자바스크립트

콜백의 개념. 비동기적과 동기적 콜백의 차이점

도도버드 2020. 5. 5. 20:45

우선 저는 엄청난 코딩 초보입니다.

그냥 제가 이해한 내용을 쓰는 것으로 틀린 부분이 있을 가능성이 있습니다.


진짜 콜백 때문에 미칠줄 알았습니다. 몇일 동안 하루 종일 책상에 앉아 공부를 해도 콜백이 도저히 이해가 안가더라고요. 그래도 겨우겨우 제가 이해한 내용들을 조금 적어볼까 합니다. 

 

제가 아는 내용을 다 적는거라 불필요하게 길 수도 있습니다.

 

 

함수는 일급객체

 

우선 시작하기 전에 함수는 일급객체(first class object)라는 점을 집고 가겠습니다.

일급객체란 변수, 매개변수에 담길 수 있고 return값으로도 사용될 수 있는 것을 가리킵니다.

 

밑에는 함수가 여러가지 형태로 사용되는 방법의 예시를 코드로 쳐봤습니다

//변수에 담긴 함수
let x = function(){
   return '나는 변수에 담겼다!';
}
x();

//변수이기 때문에 객체의 속성에 담길 수 있다. 이것을 메소드라고 부름.
let exampleObj = {
   a : function(){return '나는 메소드!'}
};
exampleObj.a();

//당연히 배열에도 사용가능함
let arr = [
   function(x,y){return x+y;},
   function(x,y){return x-y;},
   function(x,y){return x*y;}
]
arr[0](1,2);

//매개변수/파라미터에 담긴함수
function one(callback,word){
	return callback(word);
}
function two(str){
	return "나는 파라미터에 담긴 " + str;
}
one(two,'함수다!'); 

//return 값으로 쓰인경우
function a(value){
	let obj={
    	'myFunc':function(){return "나는";},
        'myFunc2':function(){return "return 값으로"},
        'myFunc3':function(){return "사용된다!"},
    };
    return obj[value];
}
a('myFunc')();   

하나하나 이해하실 필요는 없고요 그냥 함수가 여기저기에 사용될 수 있구나~ 라는 포인트만 알고 있으시면 됩니다.

(탭키 여백이 왜이리 커보일까나?ㅠㅠ)

 

저희는 일급객체인 함수가 매개변수/파라미터로 전달이 될 수 있다는 사실을 꼭꼭 기억하고 다음으로 넘어가야합니다.

 

이제 콜백을 공부해볼까요?  

 

콜백의 정의

우선 인터넷을 뒤지다 보니 대표적으로 두가지 정의를 찾을 수 있었습니다.

 

1. 다른 함수의 매개변수로 전달되어 특정 시점에서 호출되는 함수
2. 어떤 이벤트가 발생했을때 호출되는 함수.

 

제 머리가 멍청한지 저는 전혀 이해를 못했습니다 ㅠㅠㅠㅠㅠㅠㅠ

 

그렇게 한시간동안 내 나쁜 머리에 대해 절망감을 뼈저리게 느낀 후, 

"일단 1번 정의에 대해서만 집중하자!" 이런 생각과 함께 공부를 다시 시작했습니다.

 

 

1번 정의) 다른 함수매개변수로 전달되어 호출되는 함수.

 

 

우선 다른 함수라는 말이 나왔으니 두개의 함수를 정의하고 밑에 호출해 봅시다.

 

function a(){
   console.log('나는 a');
}//첫번째 함수

function b(){
   console.log('나는 b');
}//두번째 함수

a();//나는 a
b();//나는 b

 

이렇게 a라는 함수와 b라는 함수를 만들었습니다. 너무 허전해 보이지 않게 간단하게 console.log도 넣었고요.

 

이제 정의를 계속해서 읽어볼까요? 다른함수의 매개변수로 전달된다고 하네요.

그럼 한번 function b()function a()의 매개변수(패러미터)로 전달해볼까요?

 

function a(callback){ //도착!
   console.log('나는 a');
}

function b(){
   console.log('나는 b');
}

a(b);// a에 b라는 인자를 보냅시다. 출발~!
b();

 

우리는 지금 b라는 함수를 a라는 함수의 인자로 전송한겁니다.

1번 정의에 따르면 다른 함수의 매개변수로 전달된 함수를 콜백이라고 부른다고하니

b는 이제 콜백함수라고 불릴자격이 생긴겁니다!  

 

이제 전달한것을 사용을 해봐야겠죠? 보낸 콜백을 a 안에서 호출하고 결과를 봅시다.

 

function a(callback){ 
   console.log('나는 a');
   callback();
}

function b(){
   console.log('나는 b');
}

a(b); //결과: 나는 a 나는 b
b();

 

b(콜백함수)를 a의 매개변수로 전달하니 a 안에서 b를 호출할 수 있게 됬습니다.

 

이제 우리는 1번 정의에 100퍼센트 만족하는 콜백을 만든겁니다!

하지만 위의 코드는 너무 쓸모가 없습니다. 이제 조금 더 쓸모있는 코드를 빠르게 쳐보겠습니다.

 

function cal(func, x, y){//계산기
  return func(x,y);
}

function add(a,b){//더하기
  return a+b;
}

console.log(cal(add,1,2));//3

 

콜백함수를 이용해 간단한 더하기 계산기를 만들어 봤습니다.

 

코드설명:

위의 코드를 보면 cal이라는 함수를 호출할때, 첫번째 인자로 add(콜백함수)를 넣고 두번째와 세번째 인자로 1과 2를 넣었습니다. 

cal 함수는 이제 add라는 콜백을 받아 func라는 매개변수에 담습니다. 숫자들을 각각 x와 y에 담았습니다.

그리고 이제 func를 호출하면? add 함수를 호출하는 효과를 가집니다!

하지만 add는 2개의 인자를 필요로 합니다 

그러므로 우리가 아까 두번째와 세번째 매개변수로 가져온 숫자들을 func의 인자로 집어 넣어줍시다.

결과는 3으로 우리가 원하는 결과가 나왔습니다.

 

이제 추가로 용어를 하나 더 소개 하자면, 콜백을 파라미터로 받는 함수를 higher-order function이라고 합니다.

higherOrderFunction(callback){} <- 이런 느낌이 되겠네요

 

축하드립니다. 당신은 이제 콜백의 반을 이해하신겁니다!

이렇게 끝나면 얼마나 좋겠다만 이제부터 더욱 어려워집니다 ㅠㅠ

 

지금까지 의문점들

 

여기까지 이해하셨다면 엄청 훌륭하십니다. 하지만 저는 여기서 두가지 의문점을 느꼈습니다.

(읽으시는 분들을 더 헷갈리게 할 수도 있으니 이해가 가지 않다면 그냥 넘어가 주세요)

 

의문점 1. 왜 함수를 인자로 넣을때 괄호()를 빼고 넣는것일까? 함수는 언제나 괄호랑 같이 있어야 되는것 아니었나?

 

우선 어떠한 함수를 괄호와 함께 사용하면 그 함수를 호출하는 것이죠? 그래서 그 함수를 호출하면 return값을 반환합니다. 그러므로 함수를 괄호와 함께 인자로 입력하면 그 함수를 전달하는 것이 아니라 함수의 return값을 전달하는 것이기 때문에 우리가 원하는 결과가 나오지 않습니다 그래서 그 함수를 담고있는 변수 또는 함수를 가리키고 있는 포인터라고 할 수 있는 함수의 이름을 넣어주게 옳습니다.

 

function a(callback){
   return callback();
}
function b(){
   return '나는 반환값';
}

a(b);//이것은 b라는 함수를 인자로 보내는 것
a(b());//이것을 '나는 반환값'이라는 함수 b의 반환값을 보내는것. 함수가 아니기에 에러가 난다. 

 

의문점 2. 콜백을 왜 사용하는 것이지? 함수안에서 함수를 사용하고 싶을 뿐이라면 밖에 있는 함수를 그냥 호출하면 되는거 아닌가?

 

우선 맞습니다. 매개변수/파라미터를 사용할 필요없이 그냥 함수안에서 다른 함수를 호출해도 결과는 같습니다. 다음과 같은 예를 봅시다.

//콜백함수를 이용한 버전
function cal(func, x, y){//계산기
  return func(x,y);
}

function add(a,b){//더하기
  return a+b;
}

cal(add,1,2);//결과 3

//콜백함수를 사용하지 않은 버전
function cal(x, y){//계산기
  return add(x,y)
}

function add(a,b){//더하기
  return a+b;
}

cal(1,2);//결과 3

파라미터 전달된 콜백함수를 호출하든, 전역 범위에 있는 함수를 그대로 호출하든 결과는 위와 같이 같습니다. 하지만 그럼에도 콜백함수를 사용하는 것이 더 좋은 이유가 있습니다.

 

우선 이유를 설명하기위해 더하기 말고 빼기 함수도 넣어봅시다.

 

//콜백함수를 이용한 버전
function cal(func, x, y){
  return func(x,y);//아무것도 안바꿈
}

function add(a,b){//더하기
  return a+b;
}
function minus(a,b){//빼기
  return a-b;
}

cal(minus,1,2);//결과 -1

//콜백함수를 사용하지 않은 버전
function cal(x, y){
  return minus(a,b)//함수안을 바꿔야함
}

function add(a,b){//더하기
  return a+b;
}
function minus(a,b){//빼기
  return a-b;
}

cal(1,2);//결과 -1

계산기 함수를 통해 빼기를 하고 싶을때 콜백을 사용한 함수는 인자만 바꾸면 됩니다. 

파라미터의 가장 큰 장점은 파라미터는 필요에 따라 다른 종류의 함수를 받을 수 있다는 점입니다. 만약 이번엔 더하기말고 곱하기가 필요해!라고 하면 우리는 계산기 함수 안를 바꿀 필요없이 전달하는 함수만 바꾸면 끝입니다.

 

반면에 콜백함수를 사용하지 않은 계산기는 만약 더하기 말고 빼기 기능이 사용하고 싶다면 계산기 안을 바꿔야합니다. 이것은 매우 비효율적이고 비추천하는 방법입니다. 

 

함수는 자판기 같은 존재입니다. 우리 누르는 버튼(인자)에 따라서 다른 음료수(결과)를 줘야합니다. 콜백함수를 사용한 자판기는 그저 버튼만 다른것을 누르면 콜라가 짠하고 나오는데 콜백함수를 사용하지 않는다면 사이다만 나오는 자판기안을 뜯어 고쳐 콜라만 나오는 자판기로 바꿔야하는 굉장히 비효율적인 행동을 해야합니다.

 

 

굉징히 길었군요. 그럼 1번정의가 실제 코딩에서는 어떻게 사용되고있는지 알아봅시다.

 

만약 배열에 대해 배우셨다면 여러가지 배열의 함수(메소드)를 배우셨을겁니다. 근데 그 함수들이 거의다 콜백함수를 사용하고 있다는 사실을 아셨습니까? 저는 몰랐습니다. 알아도 한귀로 흘러듣고 그냥 "마법"이 일어나서 알아서 되나보구나 라고 생각했습니다. 

 

그럼 실제 예제를 들어봅시다.

let arr = [1,2,3,4]

arr.forEach(function(elem){
	console.log(elem,'출력됬음!')
});

/* 
결과:
1 출력됬음
2 출력됬음
3 출력됬음
4 출력됬음
*/

위 예제를 보니 forEach라는 배열의 내장함수는 인자로 다른 함수를 받습니다. 인자로 전달되는 함수는 뭐다? 그렇습니다 콜백함수입니다.

 

forEach에 대해서 더 설명하자면 forEach는 배열의 요소들의 값을 차례대로 콜백함수에 주면서 "그 결과 가지고 더하든 빼든 다른 배열에 넣든 맘대로 하렴"이라고 하는 것입니다. 내장된 forEach 함수를 그냥 for loop으로 풀어서 보면 더 이해하기 쉬우실겁니다

 

let arr = [1,2,3,4];
let arr2 = [];
function fakeForEach(callback){
   for(let i=0; i<arr.length; i++){
      callback(arr[i]);
   }
}

function addOne(elem){
   console.log(elem+1);
}
function pushDouble(elem){
   arr2.push(elem*2);
}

fakeForEach(addOne);//결과 2,3,4,5
fakeForEach(pushDouble);
console.log(arr2);//결과 [2,4,6,8]

 가짜 forEach를 만들어 봤습니다.

 

위에 보면 fakeForEach는 파라미터(매개변수라는 말이 입에 안붙어 이제부터 파라미터라고 하겠습니다)로 callback이라는 함수를 받습니다. 그리고 for loop으로 arr 배열의 요소들을 하나씩 callback함수의 인자로 넣습니다. callback함수가 받은 요소들로 무엇할지는 자유입니다. 위처럼 더하기 1을 해도 되고 push 메소드를 이용해 곱하기 2한 값을 다른 배열에 집어 넣어도 됩니다. 이처럼 콜백함수를 이용하여 배열의 요소들로 하고 싶은 여러 일들을 하나의 함수로 이룰 수 있습니다. (100개의 함수가 필요할지도 모르는 과정을 하나의 higher-order함수와 콜백함수들로 단축시켰습니다)

 

1번 정의를 이해하셨나요? 다시 한번 말하자면 콜백은 다른함수의 매개변수로 전달되어 특정한 시점에 호출되는 함수를 뜻합니다.

 

이제 2번정의에 대해서 알아볼까요?

 

2. 어떤 이벤트가 발생했을때 호출되는 함수.

 

우선 이벤트란? 마우스 클릭, 키보드 입력, 스크롤 등 브라우저에서 일어나는 모든일을 뜻합니다.

우리는 그 수많은 이벤트 중 특정한 이벤트들이 일어 났을때 어떠한 일이 일어나기를 바랍니다.

예를 들자면 사용자가 유튜브 플레이 버튼을 눌렀을때 동영상이 재생이 되는것처럼 말이죠.

그리고 우리는 이때에 콜백함수를 아주 요긴하게 사용할 수 있습니다.

 

이러한 코드를 짜봅시다.

"press" 버튼을 클릭했을때 버트의 색을 분홍색으로 바꾸는 코드.

 

<html>
<body>

<button id="press">press</button>

</body>
</html>

우선 html에 press라는 id를 가진 버튼을 만들어주고요.

const pressBtn = document.getElementById('press');//버튼을 (오브젝트로) 선택함

pressBtn.addEventListener('click',function(){//클릭을하면 두번째 인자인 함수를 실행함
   pressBtn.style.background="pink";
})

자바스크립트에서 버튼을 우선 선택해주고.

버튼에 이벤트리스너를 통해 클릭 이벤트를 달아줍시다.

그리고 클릭이라는 이벤트가 발생했을때 어떤 일이 일어날까요? 바로 두번째 인자에 있는 함수가 호출되어 버튼의 색을 바꿉니다.

 

그러므로 어떠한 이벤트(버튼 클릭)가 일어나면 호출되는 함수(버튼색을 바꾼다)라는 정의에 딱 들어맞기 때문에 두번쨰 인자의 있는 저 함수는 콜백함수가 맞습니다. 

 

그럼 벌써 콜백 함수의 두번째 정의를 전부 설명한거 같습니다. 

 

다시 한번 콜백의 정의를 보여드리겠습니다.

 

1. 다른 함수의 매개변수로 전달되어 호출되는 함수
2. 어떤 이벤트가 발생했을때 호출되는 함수.

 

근데 유심히 보다 보면 한가진 이상한 점이 발견됩니다. 

 

2번정의로 다시 돌아가서 예제를 보자면

pressBtn.addEventListener('click',myFunc)

function myFunc(){
//클릭하면 이것이 호출됨 
}

여기서 myFunc이라는 함수는 클릭이 발생했을때 호출되는 함수니 2번정의에 맞지만

 

myFunc은 다른 함수(addEventListener)의 매개변수로 전달되어 호출되는 함수이기도 하니 1번정의에도 딱 들어맞습니다.

 

실은 모든 콜백함수가 1번 정의를 충족합니다만 모든 콜백함수가 2번정의를 충족하지는 않습니다. 

그러므로 몇몇 콜백함수들은 다른 콜백함수들과는 다른 성질을 갖고 있는것을 깨달을 수 있습니다.

 

그렇습니다. 콜백함수는 두가지로 나뉘어져 있습니다. 

 

 

 

1. 동기적 콜백

2. 비동기적 콜백

 

헉! 동기적 비동기적? 그게 뭐지? 이런 생각이 드셨다면 걱정마세요. 지금부터 천천히 설명하겠습니다.

동기적 콜백과 비동기적 콜백을 알기전에 동기적과 비동기적이라는 단어를 알아봅시다.

 

동기적과 비동기적의 뜻

 

1.동기적

어떤 작업을 요청했을 때 그 작업이 종료될때 까지 기다린 후 다음 작업을 수행하는 방식.

 

for(let i=0; i<10; i++){//먼저 실행
   console.log(i);
}

console.log('끝!');//for loop이 끝나는 것을 기다렸다 실행

위 코드를 보자면 당연히 for loop 에서 모든 반복을 끝낸 후에 밑에있는 "끝!"이라는 것이 실행되겠죠?

 

이러한 방식을 동기적이라고 합니다. 위에있는 작업이 끝나면 다음 작업 또 끝나면 그 다음 작업 이런식으로요. 쉽죠?

 

2. 비동기적

어떤 작업을 요청했을 때 그 작업이 종료될때 까지 기다리지 않고 다른 작업을 하고 있다가, 요청했던 작업이 종료되면 그에 대한 추가 작업을 수행하는 방식.

 

비동기적의 예제는 비동기식 콜백을 설명하며 보겠습니다

 

비유를 하자면 동기적은 현금 인출기 같은 것입니다.

현금 인출기가 하나밖에 없다고 합시다. 현금이 필요한 사람들이 쭉 줄을 서있으면 제일 앞에 있는 사람부터 돈을 뽑고 그 다음사람이 차례대로 돈을 인출하는 그런 시스템입니다. 앞에 사람이 고민하느라 시간을 끌면 그냥 기다리는 수 밖에 없죠. 

 

비동기적은 햄버거 가게를 생각하시면 됩니다. 우선 햄버거를 시켜놓고 친구들과 떠들거나 편의점가서 음료수를 사거나 이것저것 하다가 카운터에서 "햄버거가 나왔습니다"하면 그때서야 햄버거를 가지러 가는 것입니다. 그러니 햄버거가 나올때까지 기다릴 동안 다른일을 할 수 있다는 장점이 있죠.

 

그럼 이제 동기적 콜백을 봅시다

 

동기적 콜백과 비동기적 콜백

1. 동기적 콜백

function calc(callback, value){
   callback(value);
}
function incOne(num){
   console.log(num+1);
}
calc(incOne,10);//먼저 실행됨
console.log("끝");//기다렸다가 다음에 실행됨
//결과: 11 끝

우선 콜백함수를 사용하여 파라미터로 받은 숫자에 1을 더하는 간단한 코드를 쳐봤습니다.

 

이 코드를 브라우저가 실행할때 순서는

1. calc와 incOne 이라는 두 함수가 정의된것을 봅니다.

2. calc(incOne,10)으로 calc함수가 호출됩니다.

3. calc 함수는 전달된 콜백함수와 10이라는 숫자를 파라미터로 받습니다.

4. calc 안에 있는 10이라는 인자를 가진 callback 함수가 호출됩니다. 

5. callback은 받은 10이라는 숫자에 1을 더한 후 11을 console에 표시합니다.

6.이 모든 과정이 끝나고 마지막으로 calc 함수 호출 다음에 있는 console.log('끝')이 실행됩니다.

 

이런식으로 한줄한줄 처리하는 것을 동기적 콜백이라고 합니다.

 

let arr = [20,2,3,1,9];
arr.sort(function(a,b){
   return a-b;
});//먼저실행됨
console.log('끝!');//기다렸다가 실행됨

 

위 코드에서도 마찬가지로 arr배열을 오름차순으로 정리하는 sort 메소드가 먼저 실행됩니다.

 

보시다시피 콜백함수들이 모든일을 전부다 처리할때까지 그 다음에 있는 작업들은 모두 기다렸다가 콜백함수의 작업이 끝나면 다음이 진행됩니다.

 

2. 비동기적 콜백

console.log('1');

setTimeout(function(){
   console.log('2');
},2000)

console.log('3');

우선 setTimeout 이란 함수는 지정한 시간만큼 (여기서는 2초) 기다렸다가 콜백함수를 실행하는 함수입니다.

 

만약 이코드를 실행한다면 어떤 결과가 나올까요?

 

우선 1 이 출력되고

2초 기다렸다가 2가 출력되고

그다음 마지막으로 3이 출력될까요?

 

아닙니다 정답은 1 3 2 입니다.

 

console.log('1');// 먼저 실행됨

setTimeout(function(){//2초동안 기달렸다가 안에 있는 함수를 실행한다. 하지만 setTimeout은 비동기적으므로 기달릴동안 밑에 있는 프로세스들이 처리됨
   console.log('2');
},2000)

console.log('3');

왜냐하면 setTimeout이 비동기적이기 때문입니다.

다시 설명하자면 비동기적이란 어떤 작업(여기서는 setTimeout의 2초 딜레이)이 종료되기를 기다리지 않고 밑에 있는 코드(여기서는 console.log(3))를 계속 실행하는 것.

 

다시 말해 밑에 있는 코드들이 바보처럼 가만히 2초동안 아무것도 안하고 있는 것이 아니라 그동안 작업을 계속 하고 있는 것입니다.

 

2초라는 시간동안 노는게 아니라 다른 작업을 실행했으니 비동기적 처리가 더 효율적이죠.

 

비동기적인 함수 setTImeOut을 때문에 콜백함수{console.log(2)}가 바로 실행 된게 아니라 2초 동안 기다린 다음에 프그그램에 의해 호출됬으므로 이 콜백함수를 비동기적 콜백이라고 합니다.

 

다른예를 봅시다

const btn = document.querySelector('#btn');

btn.addEventListener('click',function(){
   console.log("나는 비동기적 콜백 안에있다!");//클릭하면 얘도 나옴
});

console.log('끝!');//이것만 나옴

이러한 코드가 있다고 해봅시다. 이걸 실행하면 어떤 결과가 나올까요?

 

끝! 이라는 결과만 나옵니다.

그리고 버튼을 클릭해야만 나중에 "나는 비동기적 콜백 안에있다!"가 실행되죠.

 

위에있는 콜백함수가 먼저 나왔는데 왜 먼저 실행이 안된걸까요?

그건 바로 콜백함수가 비동기적 함수 addEventListener 안에 있기 때문입니다.

그래서 어떠한 이벤트(여기서는 버튼 클릭)가 일어날때까지 콜백함수는 실행되지 않습니다(이러한 특징을 가졌기에 이 콜백함수도 비동기적 콜백으로 분류됩니다).

그럼 콜백함수가 실행되지 않고 있으니 나머지 코드들은 바보처럼 기다리고 있을까요? 아닙니다.

비동기적이기 때문에 나머지 코드들도 계속 처리됩니다.

 

그러므로 콜백은 자신이 원한다고 비동기적이 되고 동기적이 되는것이 아닙니다. 어떠한 함수가 콜백을 인자로 사용하고 있느냐에 따라 콜백의 성질이 달라지는 것입니다.

 

콜백을 회사원으로 보면 악덩사장님(higher-order 함수)이 바로 일하라고 명령하면 동기적 좀 기다렸다 일하라고하면 비동기적이 되는것입니다.

 

 그럼 한가지 더 예를 들고 다음 주제로 넘어가겠습니다

let xhr = new xmlHttpRequest();
xhr.open('GET',"http://example.com/1.json",true);
xhr.onreadystatechange = function(){
   if(xhr.readystate ==4 && xhr.status==200){
      console.log(JSON.parse(xhr.responseText));//서버로부터 데이터를 받고 난 후 마지막에 나옴
   }
}

xhr.send();

console.log('끝');//먼저 나옴

AJAX를 사용해서 서버로부터 데이터를 가져오는 코드입니다.

AJAX는 비동기식 방식을 사용하므로 send()가 request를 보내고 서버로 부터 데이터를 가져오고 있을동안 다음에 있는 코드들을 계속 기다리지 않고 우선 처리가 됩니다.

 

그리고 onreadystatechange안에 있는 콜백함수는 바로 실행되는 것이 아닌 readystate 바뀌는 이벤트가 일어날때만 실행되므로 비동기적 콜백입니다. 그리고 마침내 readystate가 4가 되어 서버로부터 데이터를 가져왔을때 콜백함수를 이용하여 가져온 데이터를 사용자가 원하는 대로 처리합니다.

 

그림으로 대충 설명하자면 이런느낌입니다.

 

비동기적 콜백의 알고리즘

 

마지막 주제를 얘기하기전에 자바스크립트가 원스레드(한번에 한가지일 밖에 못함)라는 것을 강조하고 싶습니다. 그럼 자바스크립트가 원스레드라면 도데체 어떻게 비동기적 방식을 사용할수 있을까? 라는 질문이 듭니다. 우리는 여기서 call stack과 callback queue 그리고 webapi라는 것을 이해해야합니다.

 

Call stack은 자바스크립트가 현재 처리하고 있는 과제들을 쌓아둔 책장이라고 생각하시면 됩니다.

우선 main()이라는 교과서를 핍니다. 오늘 숙제는 2단원 3단원 그리고 4단원이라고 합시다.

그럼 우선 main()이라는 교과서 위에 2단원이 들어가고 2단원 숙제가 끝났으면 2단원은 날아갑니다(POP).

이제 3단원을 할 차례인데 왠지 3단원은 이따 과외 선생님이 오는 이벤트가 발생해야 처리 할수 있습니다. 

그래서 call stack이라는 책장에서 3단원을 잠깐 WEB API라는 것에게 맡겨 둡니다. 그리고 과외 선생님이 오는 이벤트를 기다리는동안 4단원 숙제를 끝내고 main교과서도 날려버리고 놀고 있습니다.

이따 과외 선생님이 오는 이벤트가 발생하면 옆에 빼둔 3단원 숙제를 callback queue라는 밀린 숙제를 담아 놓는 곳으로 이동합니다. 그리고 callback queue에 먼저 도착한 숙제부터 call stack으로 도착해 숙제를 처리합니다.  

 

그래서 결국은 자바스크립트가 여러가지 일을 동시에 하는것처럼 보여도 사실 한순간에 한가지 일만하는 녀석인것을 깨달을 수 있습니다. setTimeout이나 이벤트처럼 비동기적 처리가 필요한것이 있다면 그들 안에 콜백함수는 실행되지 않고 web api에게 맡겨집니다 시간이 다되거나 이벤트가 일어나거나 서버에서 데이터가 잘 도착했다는 소식이 도착했다는등 그들이 이제 call stack으로 돌아갈 준비가 됬다는 일이 일어나면 콜백함수들은 이제 callback queue라는 곳으로 이동합니다 이곳에서 call stack에 모든 main()을 포함한 모든 프로세스가 종료되길 기다렸다가 callback queue에 도착한 순서대로 처리가 됩니다. 그래서 비동기적 콜백은 나간 순서대로 다시 들어오늘게 아니라 먼저 callback quere에 도착한 순서대로 처라됩니다.

 

console.log('a');

setTimeout(function(){//타임아웃 시간이 0초라고 해도 제일 마지막에 처리됨
console.log('b');
},0);

console.log('c')

아까 말했듯이 call stack에 있는 모든 것들이 처리되고 난 후 callback queue가 진행되기 때문에 딜레이 시간이 0초라고해도 제일 마지막에 처리된다.

 

console.log('a');

setTimeout(function(){
   console.log('b보다 먼저 나간 콜백')
},2000);

setTimeout(function(){
   console.log('b');
},0);

console.log('c');

첫번째 setTimeout이 두번째 보다 더 먼저 call stack을 나가지만 두번째 setTimeout이 더 빨리 처리되어 먼저 callback queue에 도착했기 때문에 b가 b보다 먼저 나간 콜백보다 더 일찍 출려됨.

 

비동기적 처리의 또다른 장점은 callback queue에서 callstack으로 로드하는동안에도 프로세스의 렌더링(동작)이 계속될 수 있어서 효율적이고 더욱더 fluid한 코드를 만들 수 있다.