JavaScript는 코드가 작성된 순서대로 작업을 처리하는 동기식 언어이다.
이전 작업이 진행 중일 때는 다음 작업을 수행하지 않고 기다린다.
이러한 동작을 단일 스레드(Single Thread), 동기(Synchronous)라고 부른다.
💁🏻♀️ JavaScript는요!
- 동기적 언어
- 단일 스레드(Single Thread)
- Blocking
하지만, 이러한 동기적 처리의 단점이 있다.
아래 사진으로 본다면, 오래 걸리는 B작업의 20초가 모두 끝나기 전까지 C작업은 진행되지 않는다.
한 작업이 너무 오래 걸리면 모든 작업이 오래 걸리게 되어 전반적인 흐름이 느려진다는 것이다.
JavaScript의 비동기 처리는 싱글 스레드 방식을 이용하며, 동기적 작업의 단점을 극복할 수 있다.
아래와 같이 A, B, C의 여러 작업을 동시에 실행시키는 방식으로, 먼저 작성된 코드의 결과를 기다리지 않고 다음 코드를 바로 실행한다.
이렇게 JavaScript로 비동기 처리를 할 시, 함수를 호출할 때 콜백함수로 해당 비동기처리의 결과값을 확인한다.
JavaScript는 setTimeout등 다양한 함수를 사용하여 비동기(asynchronous)동작을 스케줄링할 수 있으며, 비동기 처리 결과값을 이용할 때는 콜백함수를 전달해 이용한다.
function taskA(a, b, cb) {
setTimeout(()=>{
const res = a + b;
cb(res);
}, 3000);
}
function taskB(a, cb) {
setTimeout(() => {
const res = a * 2;
cb(res);
}, 1000);
}
function taskC(a, cb) {
setTimeout(() => {
const res = a * -1;
cb(res);
}, 2000);
}
taskA(3, 4, (res) => {
console.log("A TASK RESULT:", res);
});
taskB(7, (res) => {
console.log("B TASK RESULT:", res);
})
taskC(14, (res) => {
console.log("C TASK RESULT:", res);
})
console.log("코드 끝");
// 코드 끝
// B TASK RESULT: 14
// C TASK RESULT: -14
// A TASK RESULT: 7
❓ taskA가 taskB보다 먼저 호출됐지만 taskB가 먼저 출력되는 이유?
🙋🏻♀️ taskA는 1초 기다리고, taskB는 3초 기다리는 작업이기 때문이다.
JavaScript는 JavaScript 엔진으로 코드를 해석한다.
JavaScript 엔진은 Heap과 Call Stack, 이 2가지 구성요소로 이루어져 있다.
위 그림은 아래의 코드가 Call Stack에 쌓여 코드가 실행되는 과정을 나타낸 것이다.
function one(){ return 1; } function two(){ return one() + 1; } function three(){ return two() + 1; } console.log(three());
- three함수를 출력하고자 했더니, three함수는 two함수를, two함수는 one함수를 return한다.
- Call Stack에서 one함수부터 제거되어가며 two함수에 1이 들어가 2가 되고, three함수에 2가 들어가 3이 되어 최종적으로 결과값 3이 출력된다.
function asyncAdd(a, b, cb){
setTimeout(() => {
const res = a + b;
cb(res);
}, 3000);
}
asyncAdd(1, 3, (res) => {
console.log("결과: ", res);
});
위 코드를 JavaScript 엔진이 어떻게 처리하는 지 알아보자.
비동기 함수인
setTimeout
은 함수를 즉시 실행시키지 않고 일정 시간 뒤에 실행하도록 실행을 지연시킨다.
Call Stack에 쌓여있던
setTimeout
은 Web APIs로 넘어가 내가 지정해둔 3초 동안 기다린다.
Call Stack에 있던
setTimeout
이 Web APIs로 넘어감에 따라,asyncAdd()
를 끝낼 수 있다. 실행을 마치면asyncAdd()
는 Call Stack에서 제거된다.
3초의 기다림 뒤, Web APIs에 있던
setTimeout()
이 제거되고, Web APIs에 있던cb()
가 Callback Queue로 옯겨진다. 이cb()
는 Event Loop에 의해 다시 Call Stack으로 옮겨질 수 있다.
Event Loop는 Call Stack에 Main Context를 제외한 다른 함수가 남아있는지 확인한다. 그리고 아무것도 남아있지 않다면 콜백함수
cb()
를 실행한다.
비동기 처리의 결과는 또 다른 비동기 처리의 값으로 전달이 가능한데, 이와 같은 과정이 많아지면 콜백지옥을 만나게 된다.
이 때, 이를 해결해줄 수 있는 JavaScript 비동기 담당 객체 Promise가 있다. 이에 대한 내용은 다음 포스팅에서 다루어 볼 예정이다!
function taskA(a, b, cb) {
setTimeout(()=>{
const res = a + b;
cb(res);
}, 3000);
}
function taskB(a, cb) {
setTimeout(() => {
const res = a * 2;
cb(res);
}, 1000);
}
function taskC(a, cb) {
setTimeout(() => {
const res = a * -1;
cb(res);
}, 2000);
}
// 콜백 지옥
taskA(4, 5, (a_res) => {
console.log("A TASK RESULT:", a_res);
taskB(a_res, (b_res) => {
console.log("B TASK RESULT:", b_res);
taskC(b_res, (c_res) => {
console.log("C TASK RESULT:", c_res);
});
});
});
console.log("코드 끝");
// 코드 끝
// A TASK RESULT: 9
// B TASK RESULT: 18
// C TASK RESULT: -18
taskA 결과값을 taskB에서 사용하고, taskB의 결과값을 taskC에서 사용하며 점점 코드의 모양이
>
형태로 안으로 깊어지는 모습을 볼 수 있다.
한입 크기로 잘라 먹는 리액트를 학습하며 개인적으로 정리한 내용입니다.
※ 그 외
자바스크립트 - 동기(Synchronous)? 비동기(asynchronous)?
모던자바스크립트 콜백