자바스크립트는 싱글 스레드 방식이다. 즉 자바스크립트를 위해 일하는 일꾼이 하나뿐이라는 것이다. 그렇게 되면 한 task가 끝날 때까지 기다렸다가 다른 task를 해야하나? 이러한 것을 동기적 작업이라 하는데, 꼭 그건 아니다.
싱글 쓰레드 방식을 이용하면서, 한 task의 끝을 기다리는 동기적 작업의 단점을 극복하기 위해 여러 개의 작업을 동시에 실행시키는 것을 비동기 작업이라고 한다. 즉, 먼저 작성된 코드의 결과를 기다리지 안고 다음 코드를 바로 실행하는 것이다.
그렇다면 각각의 task가 끝났다는 것은 어떻게 아나?
바로 콜백함수를 붙여주면 된다.
taskA((resultA) => {console.log(${resultA})};와 같은 식으로
할일을 다하면 결과값을 호출하게 하여, 이 결과값을 이용하게 하는 것이다.
비동기적 방식을 이용한 코드는 다음과 같다.
function taskA(a, b, cb){
setTimeout(()=>{
const res = a + b;
cb(res);
}, 3000);
}
taskA(3,4, (res) => {
console.log(res);
});
console.log("Finish");
먼저 taskA를 수행하려면 3초를 기다려야 하므로 Finish가 먼저 콘솔에 뜰 것이다. 그런 뒤 taskA가 반환될 것이다.
동기, 비동기의 방식을 이해하기 위해 자바스크립트의 구동 방식을 간단히 이해해보자.
자바스크립트의 엔진은 다음과 같이 구성된다.
-Heap: 메모리 할당
-Call Stack: 코드 실행
1) 비동기 방식
자바스크립트 코드의 실행이 시작되면 Call Stack에 main context가 들어오고 차례로 시행되게 된다.

위의 코드의 경우, 실질적으로 수행하는 코드인 three()부터 콜스택에 들어온다. three()를 위해서는 two()가 필요하고, two()를 위해서는 one()이 필요하므로 차례로 콜스택에 들어온다.
그런 뒤에 one()에 해당하는 값이 반환되어 콜스택을 나가고, two(), three도 마찬가지 과정을 거치면서 콜스택을 나간다.

마지막으로 콘솔창에 3이 띄워지면서 main context까지 콜스택을 빠져나오며 프로그램은 종료된다.
2) 동기 방식

실질적으로 시행되어야 하는 함수인 asyncAdd가 먼저 콜스택을 들어온다. setTimeout()은 들어왔다가 시간이 걸리는 함수이므로 Web API로 옮겨진다.

그동안 asyncAdd()가 시행되어 종료된다.

Web API에서 시행을 완료한 setTimeout()은 cb()남긴다. cb()은 Callback Queue로 이동하며, Event Loop를 통해 다시 콜스택으로 진입하여 시행된다. 그리고, 프로그램이 종료된다.
다음은 비동기 프로그램이다.
function taskA(a, b, cb){
setTimeout(()=>{
const res = a+b;
cb(res);
}, 1000);
}
function taskB(a, cb){
setTimeout(() => {
const res = a * 2;
cb(res);
}, 1000);
}
function taskB(a, cb){
setTimeout(() => {
const res = a * 2;
cb(res);
}, 2000);
}
taskA(4, 5, (a_res) => {
console.log(a_res);
taskB(a_res, (b_res)=>{
console.log(b_res);
tadkC(b_res, (c_res) =>{
console.log(c_res);
});
});
});
console.log("Finish");
Finish, a_res, b_res, c_res 순서로 콘솔창에 뜨게 된다.
\
위는 taskA에서 만들어진 a_res를 이용하여 taskB를 시행하고, taskB에서 만들어진 b_res를 이용하여 taskC를 시행하는 코드이다. 실제 프로그램에서는 앞의 함수에서 나온 값(콜백)을 이용하여 다음 함수를 시행하는 경우가 많을 수 밖에 없다. 그렇다면 매번 이렇게 엄청난 콜백지옥으로 코드를 짜야하는 것일까? 아니다. 이를 보완하기 위해 promise를 사용할 수 있다.