동기, 비동기의 동작원리
동기(Synchronous), 비동기(asynchronous) 정의
자바스크립트는 싱글 스레드 프로그래밍 언어이며 동기 방식으로 코드를 해석한다.
먼저 싱글 스레드란 하나의 프로그램은 동시에 하나의 코드만 실행할 수 있다는 것을 말한다.
그렇다면 동기 방식은 무엇일까?
동기(Synchronous)
Synchronous : 동시에 발생하는
순차적/직렬적으로 태스크를 수행한다.
요청을 보냈다면, 응답을 받아야 다음 동작이 이루어진다.
순차적으로 실행되므로, 어떤 작업이 수행중이라면 뒤의 작업은 대기한다.
블로킹(작업 중단)이 발생한다.
비동기(asynchronous)
Asynchronous : 동시에 발생하지 않는
병렬적으로 태스크를 수행한다.
현재 작업의 종료여부와 무관하게 다음 작업을 실행한다.
그러므로 동기 방식과는 달리 완료 순서가 보장되지 않는다.
블로킹이 발생하지 않는다.
위의 그림으로 좀 더 쉽게 이해할 수 있다.
동기 방식은 손님이 주문을 하고 » 커피가 나올 때 까지 기다려서 » 커피를 받아야 한다.
비동기 방식은 우선 주문을 받고 » 손님은 커피가 나오는 순서를 신경쓰지 않고 » 진동벨이 울리면 커피를 받는다.
자바스크립트는 기본적으로 코드를 순차적으로 처리한다.
먼저 자바스크립트 엔진이 순차적(동기)으로 처리하는 동작 과정을 알아보자.
동기(Synchronous) 동작 원리
자바스크립트 엔진이 동기 처리에서 어떻게 동작하는지 알아보기 전에 자바스크립트의 엔진의 주요 구성 요소인 Memory Heap와 Call Stack에 대해 먼저 알아보자.
* 자바스크립트의 엔진의 주요 구성 요소
Memory Heap이란?
변수와 객체의 메모리 할당을 담당하는 곳을 말한다.
Call Stack이란?
함수가 호출이 되면 쌓이는 곳이다. 대신 함수가 쌓이는 순서와는 반대로 실행된다.
자바스크립트에서 함수를 호출하면 Call Stack이라는 곳에 호출 순서대로 차곡차곡 쌓인다. 그러고 나서, Stack은 맨 마지막에 호출된 함수가 맨 먼저 반환한다.
List in Frist Out, 먼저 들어온 것이 먼저 나간다라는 의미로, LIFO 구조라고 부른다.
Call Stack은 비워진 상태로 있다가 함수가 호출되면 Stack(스택)에 해당하는 함수를 집어넣게 되고, 함수에서 리턴이 일어나면, 스택의 가장 위쪽에서 해당 함수를 꺼내게 된다.(LIFO)
이 코드를 실행하면 실행되는 코드 자체를 말하는 main() 함수를 스택에 집어넣게 된다.
그러면 이제 main() 안에 들어있는 함수들이 정의가 된다.
마지막 줄에 printSquare(4)를 만나게 되는데, 여기서 함수를 호출하고 있으니 스택에 함수를 추가한다.
printSquare() 함수는 square() 함수를 호출, square() 함수는 multiply()함수를 호출하고 있으므로 Stack(스택)에 순서대로 쌓이게 된다.
이제 함수 내에서 무언가를 return할 때마다 우리는 스택 위에서 하나씩 꺼내게 된다.
즉, pop()을 하게 된다.
먼저 multiply() 함수가 a * b를 반환하고 Stack(스택)에서 사라진다.
그러고 나서, square() 함수가 multiply()가 반환한 값 a * b를 가지고 반환하고 Stack(스택)에서 사라진다.
printSquare()에서 square() 함수가 종료되어 그 다음줄의 console.log(squared)가 Stack(스택)에 쌓인다.
그 다음, console.log(squared) 실행되어 console에 squared가 출력되고 Stack(스택)에서 사라진다.
printSquare()까지 모두 종료되었기 때문에 main()도 Stack(스택)에서 사라진다.
지금까지 자바스크립트 엔진이 동기 처리에서 어떻게 동작하는지 알아보았다.
자바스크립트는 기본적으로 코드를 순차적으로 처리한다고 하였다.
하지만 우리가 코드를 작성하다 보면 비동기적으로 실행되는 부분이 있지 않았나?? 생각이 들 수 있다.
- 비동기적으로 처리되는 상황 예시
- 특정 서버에 요청을 보내서 응답을 가져와야 한다.
- 일정한 시간 뒤에 특정 코드를 수행해야 한다.
이 작업들의 공통점은 빠르게 처리되지 않고 작업이 언제 끝날지 예측이 힘들다는 것이다.
그래서 자바스크립트 엔진은 “무거운 작업”을 어딘가에 위임하고, “동기적으로” 바로 다음 코드를 실행할 수 있다.
그 과정에 대해 알아보자.
비동기(asynchronous) 동작 원리
비동기 처리를 하기 위해서는 Memory Heap와 Call Stack외에 Web API, Task Queue, Event Loop 에 대한 개념이 추가된다.
Web API이란?
Ajax 요청, setTimeout(), 이벤트 핸들러의 등록과 같이 웹 브라우저에서 제공하는 기능들을 말한다.
Task Queue이란?
setTimeout이나 setInterval과 같은 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역
Event Loop이란?
이벤트 루프는 콜 스택에 현재 실행 중인 실행 컨텍스트가 있는지, 그리고 콜백 큐에 대기 중인 함수(콜백 함수, 이벤트 핸들러 등)가 있는지 반복해서 확인한다.
만약 콜 스택이 비어 있고 태스크 큐에 대기 중인 함수가 있다면 이벤트 루프는 순차적(FIFO, First In First Out)으로 콜백 큐에 대기 중인 함수를 콜 스택으로 이동시킨다.
이 코드 역시 실행하면 실행되는 코드 자체를 말하는 main() 함수를 스택에 집어넣게 된다.
그러고 나서, console.log(hi)가 호출되어 stack(스택)에 쌓이고 실행되어 hi가 콘솔 창에 출력된다.
hi가 출력이 되면, stack(스택)에서 console.log(hi)가 사라지고, setTimeout을 실행한다.
여기서 setTimeout()는 브라우저에서 제공하는 API(web API라고도 한다)이다.
즉, 이 말은 자바스크립트가 실행되는 런타임 환경에서 별도의 API가 존재한다는 뜻이다.
브라우저가 web API에서 타이머를 실행시키고 카운트 다운을 시작한다.
이건 setTimeout 호출 자체가 완료되었다는 의미이므로, 이제 stack(스택)에서 setTimeout()은 사라진다.
그러고 나서, console.log(‘JSConfEU’)가 호출되어 stack(스택)에 추가되고, ‘JSConfEU’를 출력 후 stack(스택)에서 지워진다.
이제 web API에서 실행하고 있는 타이머만 남았다.
하지만, 이 타이머는 5초 뒤에 종료되는데, 갑자기 작성된 코드에 끼어들면 절대 안 된다.
이제 여기서 task queue 또는 callback queue가 활약할 차례이다.
모든 Web API는 작동이 완료되면 task queue에 쌓이게 된다.
event loop는 stack(스택)이 비어있을 경우에 task queue의 첫 번째 call back부터 stack(스택)에 쌓아 효과적으로 실행할 수 있게 해 준다.
setTimeout()의 콜백 함수는 드디어 실행되고 console.log()를 실행하여 console에 ‘there’을 출력하며 stack에서 모두 사라진다.
📑 참고자료