브라우저 런타임 환경의 이해(1) - Javascript Engine, Event Loop, Web APIs


  • Javascript Engine과 Web APIs, Event Loop에 대해서 정리한 내용입니다.

  • 멀티쓰레딩은 한 프로세스 안에서 여러가지 쓰레드가 동시 다발적으로 일어나는 것을 말한다

  • 멀티 쓰레드를 가지고 있으면 동시에 여러가지 일을 수행할 수 있기 때문에 효율적으로 프로그램이 동작할 수 있다

  • 자바(Java)의 경우 언어 자체에서 멀티쓰레딩이 지원이 된다

  • 따라서 특정한 데이터를 받아오는 작업은 쓰레드 A에서, 또 다른 작업은 쓰레드 B에서 시키는 방법으로 각각 수행할 작업을 쓰레드에 지정할 수 있고 총 몇개의 쓰레드가 동작하는지와 같이 멀티쓰레딩과 관련된 다양한 작업을 수행할 수 있다

  • 하지만 자바스크립트는 싱글 스레드 언어(SIngle Thread Language)이기 때문에 언어 자체에는 멀티쓰레딩이 없다

  • 자바스크립트 언어 자체에는 멀티쓰레딩을 할 수 있는 방법은 없지만 자바스크립트가 동작하고 있는 브라우저 위에는 여러가지 쓰레드가 들어있다

  • 그래서 브라우저의 웹 API들을 이용하게 되면 멀티쓰레딩이 가능하다

  • 그리고 자바스크립트가 동작한느 런타임 환경, 즉, 자바스크립트가 동작하고 있는 실행 환경에서는 다양한 방식을 이용해서 멀티쓰레딩 같은 효과를 얻을 수 있고 뿐만 아니라 이벤트 루프를 이용해서 더 다양한 동작을 실행할 수 있다

JavaScript Engine

  • 웹 어플리케이션이 브라우저 위에 올라가는 순간 자바스크립트 엔진이 작성한 소스코드를 한줄 씩 해석하고 실행하게 된다.

  • 자바스크립트 엔진의 구조는 크게 메모리 힙과 콜 스택으로 나누어져 있다

  • 프로세스처럼 자바스크립트 엔진에도 메모리 힙과 콜 스택이 있다

  • Memory Heap

    • 변수를 선언해서 객체를 할당하거나 문자열, 숫자를 할당하게 되면 그 데이터들은 전부 다 메모리 힙에 저장된다

    • 메모리 힙은 구조적으로 정리된 자료구조가 아니라 자료들이 여기저기에 아무곳에나 저장이 되어져 있다

  • Call Stack

    • 함수를 실행하는 순서에 따라 차곡차곡 스택에 쌓이게 된다

    • 스택은 LIFO(Last In First Out) 구조를 가지기 때문에, 가장 마지막으로 들어간 요소가 가장 먼저 나오게 된다

    • 예를 들어, 다음과 같은 순서로 함수를 호출하게 되면 스택에는 fisrt 함수, second 함수 그리고 third 함수가 순서대로 쌓이게 된다

    function third() {
      console.log('Third');
      return;
    }
    function second() {
      third();
      return;
    }
    function first() {
      second();
      return;
    }
    first();
    
    • third 함수에서 리턴을 하면 콜 스택을 보게 되는데 콜 스택에는 어디로 돌아와야 되는지에 대한 정보도 포함이 되어있기 때문이다

    • 따라서 third 함수에서 리턴을 하면 third 함수 호출이 끝난 곳으로 돌아가게 된다

    function third() {
      console.log('Third');
      return;
    }
    function second() {
      third();
      // third 함수에서 return 되면 이곳으로 이동한다
      return;
    }
    function first() {
      second();
      return;
    }
    first();
    
    • 그리고 second 함수도 리턴이 호출되었기 때문에 second 함수의 호출이 끝난 곳으로 돌아가게 된다

    • 그리고 first 함수도 리턴이 호출되기 때문에 first 함수의 호출이 끝난 곳으로 돌아가게 되고 콜 스택에는 아무 것도 남아있지 않게 된다

    function third() {
      console.log('Third');
      return;
    }
    function second() {
      third();
      // third 함수에서 return 되면 이곳으로 이동한다
      return;
    }
    function first() {
      second();
      // second 함수에서 return 되면 이곳으로 이동한다
      return;
    }
    first();
    //first 함수에서 return 되면 이곳으로 이동한다
    
    • 콜 스택은 함수들이 호출하는 순서를 기억했다가 함수가 끝나면 원래 있던 자리로 들어가기 위해서 쓰이는 자료구조 중 하나이다.

    • 그리고 모든 프로세스와 스레드 안에는 각가 저마다의 콜 스택이 들어가 있다

    • 일을 수행할 때 어디서 왔고 어디로 가야 되는지 정보를 기억해야 하기 때문이다

    • 다음과 같은 코드처럼 함수 안에서 자기 자신을 계속해서 호출하게 되는 재귀 함수의 경우 콜 스택에는 계속해서 endless() 가 쌓이게 되고 결국 Maximum call stack size exceeded라는 에러 메시지가 호출된다

    function endless() {
      endless();
    }
    
    endless();
    
    • 프로세스마다, 스택 마다 지정된 콜 스택 사이즈가 있기 때문에 지정된 콜 스택 사이즈를 초과해서 에러 메시지가 발생하게 된 것이다

Web APIs & Event Loop

  • Javascript Engine, Web APIs, Event Loop는 다음과 같은 관계를 가진다

  • WEB API는 브라우저에서 제공하는 API이기 때문에 브라우저의 멀티스레딩을 통해서 다양한 일들을 동시에 실행할 수 있다

  • 예를 들어, third() 함수 안에서 setTimeout을 호출하고, setTimeout에는 3초뒤 ‘hello’를 출력하는 콜백함수를 등록한다고 했을 때, setTimeout을 호출하는 순간 setTimeout은 콜 스택에서 지워지고, Web APIs는 타이머를 시작하게 된다

  • 타이머가 실행되고 있는동안에도 자바스크립트 엔진은 병렬적으로 실행이 되며, 지정된 시간이 끝나면 Web APIs는 태스크 큐에 setTimeout에서 등록한 콜백함수를 넣는다.

  • 콜 스택과 태스크 큐 사이를 계속해서 관찰하는 이벤트 루프가 있는데. 콜 스택에 해야할 일들이 남아 있으면 콜 스택이 비워질 때 까지 기다린 다음에, 콜 스택이 다 비어서 자바스크립트 엔진이 더 이상 일을하고 있지 않을 때 태스크 큐에 있는 것들을 하나씩 콜 스택에 가져온다. 그러면 자바스크립트 엔진은 이를 실행하게 된다

  • 따라서, 콜 스택이 비어져 있다면, 콜 스택에 있는 setTimeout에 등록된 콜백 함수가 이벤트 루프를 통해서 콜 스택으로 옮겨지게 되고, 자바스크립트 엔진은 이를 실행하게 된다

  • 이벤트 루프는 프로세스가 동작하는 동안 계속해서 루프를 돌면서 콜 스택이 비어져 있다면 태스크 큐에 있는 것을 한번에 하나씩 콜 스택으로 가져와서 자바스크립트 엔진이 수행할 수 있도록 도와준다

  • 그리고 콜 스택이 비어지면 다시 태스크 큐에 있는 것을 하나씩 콜 스택으로 가져온다

  • 만약 자바스크립트 코드의 버튼 태그에 이벤트 리스너를 등록해 놓으면, 클릭이 일어나는 순간, Web APIs는 우리가 등록한 콜백함수를 이벤트 큐에 넣게된다.

  • 그리고 또 한번 버튼이 클릭되면, 등록한 콜백함수가 또 다시 이벤트 큐에 들어가게 된다


Reference









© 2020. by dkmqflx

Powered by dkmqflx