[JavaScript] 동기와 비동기

혜린·2022년 6월 27일
0

JavaScript

목록 보기
11/21
post-thumbnail

동기적 언어, JavaScript


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초 기다리는 작업이기 때문이다.



JS엔진의 동기, 비동기 구분법


JavaScript는 JavaScript 엔진으로 코드를 해석한다.
JavaScript 엔진HeapCall Stack, 이 2가지 구성요소로 이루어져 있다.


(1) 동기처리 방식

위 그림은 아래의 코드가 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이 출력된다.

(2) 비동기 처리 방식

function asyncAdd(a, b, cb){
	setTimeout(() => {
    	const res = a + b;
      	cb(res);
    }, 3000);
}
asyncAdd(1, 3, (res) => {
	console.log("결과: ", res);
});

위 코드를 JavaScript 엔진이 어떻게 처리하는 지 알아보자.


비동기 함수인 setTimeout은 함수를 즉시 실행시키지 않고 일정 시간 뒤에 실행하도록 실행을 지연시킨다.


Call Stack에 쌓여있던 setTimeoutWeb 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)?
모던자바스크립트 콜백

profile
FE Developer

0개의 댓글