Sync/ Async

sangminnn·2020년 4월 4일
0

글로 정리하는 JS

목록 보기
3/5

JS 개념에 대해서 공부하다보면 항상 여기저기서 심심치않게 자주 나타나는 용어가 있다.

그것은 비동기 에 대한 내용이다.

그러면 비동기의 반대 개념인 동기와는 어떤 차이가 있고, 정확히 두 동작이 어떻게 진행되는지에 대해 의문이 생겼다.

자주 나오는 만큼 확실하게 개념을 잡고 넘어가 보는게 좋겠다는 생각으로 여러가지 글을 찾아보고 공부해 보았다.


기본 개념

단순하게 일단 기본 개념부터 보자.

' 동기 ' 방식은 이전 글중 하나인 Promise, async/ await 에서도 간단하게 언급한 적이 있다.

그때 언급한 내용을 잠시 들고와보자면

js의 기본 동작은 'Run to Complete' , 즉 하나의 task가 끝나면 다음 task를 실행하는 동기식 처리방식인데,

이보다 더 많은 동작을 하기 위해서는 다른 동작을 하면서 동시에 실행할 수 있는 비동기식 처리방식을 선호할 수 밖에 없다.

JS에는 이를 구현하기 위한 패턴으로 보통 Callback 함수를 사용하였다.

위 글을 읽어보면 JS의 기본동작은

하나의 동작이 진행되는 동안은 그 동작만을 처리하다가 그 동작을 완료하면 그 다음에 해결해야할 문제로 넘어가는 방식이다.

간단하게 비유하자면, 수학문제를 풀다가 어렵다고 넘어가는것이 아니라. 그 문제를 다풀때까지 붙잡고있다가 해결하면 다음 문제로 넘어가는 것이 동기식 처리방식이라고 할 수 있겠다.

비동기식 처리방식으로는 내가 수학문제를 푸는 동작에 영향을 미치지 않으면서, 내 몸 안에서는 이전에 먹은 음식을 소화시키며 이에 대해 배출할 수 있도록하는 소화 과정을 내부적으로 진행하고 그 내부 동작이 결국 '배출' 이라는 외부 동작으로 나타나게 하는 것이라고 할 수 있겠다.

간단한 개념은 봤으니 정확한 동작을 지켜보자

정확한 동작을 알기위해서 먼저 알고가야할게 있기 때문에 잠시만 미뤄두고 이것부터 알아보고 가자.


우리가 사용하는 JavaScript는 기본적으로 스크립트 언어인데, 스크립트 언어라는 것은 그 언어를 해석해주는 interpreter가 있어야한다는 것이다.

그리고 이 interpreter의 내부에는 Call Stack, Task Queue, Heap 이라는 3가지 구성 요소가 존재한다.

이는 브라우저의 구조에 관한 그림이다.

이 그림을 보면 하단에 자바스크립트 해석기 가 존재한다는 것을 알 수 있고, 따라서 브라우저에서는 자바스크립트 해석기가 존재하기 때문에 Javascript 를 사용할 수 있다는 것이다.

이 정도만 인지하고 이제 진짜 동작에 대해 알아보러 가자.


동작

JS의 동작을 처리해주는 것에는 Call Stack(호출 스택)이라는 것을 중심으로 동작하는데 이 호출 스택에는 기본적으로 동기적 실행을 처리하는 내용들이 쌓인다.

비동기 동작이 들어올 경우 이에 대한 내용은 Call Stack 에 쌓는게 아니라, 비동기 동작에 관한 것은 Web API에서 처리한다.

Web API에서는 비동기 동작에 대한 내용을 처리하면서 완료된 동작에 대해서는 Task Queue(Event Queue)에 쌓아둔다.

그리고 동기식 동작에 대한 처리가 모두 끝마쳤을 때, Task Queue에 있는 내용을 하나씩 Call Stack이 받아오고, 이에 대한 동작을 진행하는 것이다.

이렇게 글로 보는것보다 코드의 동작으로 보는게 보다 직관적으로 이해가 될 것 같아 간단한 예제를 준비했다.

    function test1() {
    	console.log('test1');
    	test2();
    }
    
    function test2() {
    	let asyncTest = setTimeout(function() {
    		console.log('test2');
    	}, 0);
    	test3();
    }
    
    function test3() {
    	console.log('test3');
    }
    
    test1();

위의 코드를 보고 동작에 대해서 생각해보자.

  1. test1 함수는 console에 test1 이라는 문자열을 출력하고, test2 함수를 실행한다.
  2. test2함수가 실행되어 먼저 asyncTest 변수에 값이 할당된다.
  3. test2함수의 동작이 setTimeout이라는 비동기 함수에 의해 비동기적으로 처리된다.
  4. 이후 test3 함수를 실행한다.

일단 전체적인 동작으로만 보았을 때는 이렇게 순서를 인지할 수 있겠다.

이제 이 코드가 어떠한 출력으로 이어지는지 자세히 보겠다.

먼저, 1번 내용은 위에서 인지한 것과 동일하다. ( console에 test1 출력)

이후 위의 2번과 3번, 4번 내용이 이 동기/비동기식 동작에 관해 알 수 있는 가장 중요한 부분이 되겠다.

일단 코드가 나열된 순서대로 생각해보면 asyncTest함수에 할당되는 동작에 따라 console창에

test2 가 출력되는 동작을 비동기적으로 처리하기 위해 Web API 에 동작이 넘어간다.

이후 Web API에서는 setTimeout의 Term이 0ms 이기 때문에 사실상 바로 동작을 처리하게 된다.

그렇게 된다면 해당 동작에 대한 완료값은 Task Queue 에 들어가게 된다.

그 다음은 동기식 동작이 먼저 다 진행된 후에 Task Queue에 들어가 있는 내용이 Call Stack 으로 이동한다고 언급했기 때문에, test3를 동기식으로 출력하는 test3 함수가 Call Stack에 들어오고, 이에 대한 출력 값이 console 창에 출력된다.

이렇게 동기식 동작이 모두 마무리 된 상태에서, 이제는 task Queue 에 있는 비동기 함수 test2의 완료동작이 Call Stack으로 이동하게 되고,

여기서 중요한 점은, 0ms라면 사실 동기방식처럼 처리가 될 정도로 바로 처리가 되는 속도임에도 불구하고 주요 동작을 실제로 처리하는 Call Stack이 아닌 Task Queue에 값이 쌓이기 때문에 test3 출력값보다 늦게 출력이 된다는 점이다.


마치며 ...

항상 비동기 함수를 볼 때면 가끔 비동기 동작에 대한 정확한 개념이 잡혀있지 않아서, 종종 헷갈리는 순간들이 있었다.

이번에 이렇게 조금은 깊이있게 공부하는 과정에서 이들에 대한 내부 동작을 알게 되었고, 그로 인해 왜 이렇게 출력될 수밖에 없었는지를 알게되면서 조금은 막연하게 사용하던 비동기 동작에 대해 친숙해 질 수 있던 시간이였다.


위에서 언급한 Javascript Engine의 3가지 구성요소들의 동작은

_Jbee 님의 https://asfirstalways.tistory.com/362 에 자세히 나와있기 때문에 보다 깊은 공부를 원한다면 한번쯤은 읽어보는 것을 추천합니다.

상단의 브라우저의 구조에 대한 그림은

https://d2.naver.com/helloworld/59361 에서 가져왔습니다.

profile
생각하며 코딩하려고 노력하는 개발자가 되겠습니다.

0개의 댓글