ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Javascript 런타임
    javascript 2022. 8. 29. 09:52
    반응형

    javascript runtime은 javascript code가 실행할 수 있는 환경을 말합니다.

    본 포스터는 하기 동영상 내용을 많이 참조하였습니다.

    https://www.youtube.com/watch?v=8aGhZQkoFbQ

    동영상 저자가 만든 tool을 사용하여 call stack, web API, callback queue, event loop 동작을 볼수 있습니다.

    latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D

     

    http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D

     

    latentflip.com

     

    Javascript Engine (V8)

    Javascript 하면 가장 먼저 떠오르는것이 Javascript Engine 입니다. (그중에서도 Google의 V8 Engine이 가장 유명합니다. 실제 Node.js 도 V8 Engine 기반으로 구현되었습니다). 그러면 Javascript Engine은 무엇을 책임질가요?

    실제 우리가 구현한 javascript code는 Javascript Engine에서 동작합니다. Javascript Engine은 다음과 같이 구성되어 있습니다.

    • Heap Memory: Memory 할당이 발생하는 곳
    • Call Stack (또는 Execution Stack 이라고도 합니다.): 실제 코드가 실행되는 곳
    • javascript 는 Single Thread 이기 때문에 Heap Memory와 Call Stack은 각각 하나만 존재합니다.

    Javascript Engine

    Call Stack 동작은 위에서 공유한 Loupe 사이트에서 하기 코드를 복사한 후 확인해보면 되겠습니다.

    function funcC() {
        console.log("funcC is executed");
    }
    
    function funcB() {
        funcC();
        console.log("funcB is executed");
    }
    
    function funcA() {
        funcB();
        console.log("funcA is executed");
    }
    
    funcA();

     

    Javascript Runtime

    1. web API (nodeJS API)

    먼저 하기 코드를 보겠습니다.

    console.log("Start");
    
    let start = new Date().getTime();
    
    setTimeout(function timeout() {
    	console.log(`Timeout: ${new Date().getTime() - start}`);
    }, 5000);
    
    while (new Date().getTime() - start <= 2000) continue;
    
    console.log("End");

    chrome console에서 실행하면 "Start" => "End" => "Timeout: 5000"(5000에서 첫자릿 수는 변할 수 있습니다.) 순으로 출력되는것을 볼수 있습니다.

    여기에서 주의할 점은 javascript는 single thread 언어 인데 5초 후에 timout 함수가 실행된다는 것입니다. setTimeout이 호출되는 순간부터 누군가가 5초 counting을 해야합니다. 만약 javascript 실행에 javascript engine만 존재한다면 main thread 하나만 존재하기 때문에 2초동안 while 문이 돌고 "End" 문구가 출력된 후 5초 counting을 하고 결과는 "Timeout: 5000"이 아니라 "Timeout: 7000" 이었을 것입니다. 즉 javascript 코드가 실행되기 위해 javascript engine 외에 어떤 module이 존재한다는 거죠.

    해당 module이 바로 browser에서는 web API, nodeJS에서는 nodeJS API 입니다.

    우리가 많이 사용하는 setTimeout, XMLHttpRequest(Ajax), DOM node 관련 API(document) 등이 모두 web API에서 제공하는 것입니다. 때문에 javascript는 single thread 언어이지만 multi-thread 인것처럼 동작할 수 있는 것입니다.

    이제 우리는 javascript 런타임은 하기와 같은 2개 모듈로 구성됨을 알수 있습니다.

    Javascrip Runtime 1

     

    2. Callback Queue (Task Queue) & Event Loop

    setTimout은 web api에서 실행한다는것을 알게 되었습니다. 그러면 5초동안 counting한 후 web api에서는 언제, 어떻게 callback 함수를 javascript call stack에 옮겨갈가요(call stack에 들어갔기 때문에 callback 함수가 실행됬겠죠)? 그냥 5초가 되면 바로 call stack 옮길가요? call stack에 실행하고 있는 코드가 있는데 앞뒤 순서없이 그냥 꽂아 넣을수는 없겠죠.

    web api에서 실행된 callback 함수를 다시 call stack에 옮기기 위하여 Callback Queue와 Event Loop라는 모듈(기술?)을 사용하게 됩니다. 다음은 전체 javascript runtime 구조입니다. 

    Callback Queue는 말그대로 web api에서 실행완료된 callback 함수들이 순서대로 들어가 잠시 대기해 있는 queue입니다.

    Event Loop는 callback queue에 있는 callback 함수들을 call stack에 집어넣는 역할을 합니다. 여기서 주의할 점은 call stack이 비어있을 때에만 event loop가 callback queue에서 대기하고 있는 callback 함수를 하나씩 꺼내서 call stack에 집어넣습니다.

    이렇게 callback queue와 event loop를 이용하여 web api에서 실행완료된 callback 함수들이 최종적으로 call stack에서 실행됩니다.

    자, 그러면 이제 하기 코드는 어떻게 실행될가요?

    console.log("Start");
    
    let start = new Date().getTime();
    
    setTimeout(function timeout() {
    	console.log(`Timeout: ${new Date().getTime() - start}`);
    }, 5000);
    
    while (new Date().getTime() - start <= 7000) {}
    
    console.log("End");

    chrome console에서 위의 코드를 실행하면 하기와 같은 결과를 확인할 수 있습니다.

    "Start" => "End" => "Timeout: 7002" 로 찍혔습니다. 분명 setTimout으로 5초후 실행되도록 설정했지만 실제로 7초 뒤에 실행되었습니다. 원인은 바로 event loop는 stack이 비었을 때 callback queue에서 callback 함수를 꺼내서 stack에 넣기 때문입니다.

    • web api에서 5초 count 완료.
    • callback queue에 timout() 함수를 넣음.
    • event loop는 stack을 봤더니 while (new Date().getTime() - start <= 7000) {} 가 실행되고 있음.
    • 1초(start후 6초 경과) 후에(예를 든 것입니다. 당연히 1초에 한번씩 관찰하지는 않겠죠.) stack을 봤더니 위의 코드가 계속 실행되고 있음.
    • 또 1초 후에 봤더니 stack이 비었음. callback queue에서 timout() 함수를 stack에 집어 넣음.
    • timout() 함수가 실행

    저는 실무에서 한번 setTimout을 분명 1000으로 설정했는데 실제 1초 이후에 실행되는 현상을 겪었습니다. 그때는 왜 이러지 하면서 다른 방법으로 넘기긴 했는데 javascript runtime을 이해하고 나서 원인을 알수 있었던것 같습니다.

    setTimout은 설정된 시간에 실행되는것을 보장하지 않습니다. 설정된 시간 이후에(minimum time to execution) 실행되는것을 보장할 뿐입니다.  

    그러면 다음 코드는 어떻게 실행될가요?

    console.log("Start");
    
    let start = new Date().getTime();
    
    setTimeout(function timeout() {
    	console.log("Timeout");
    }, 0);
    
    console.log("End");

    하기와 같이 "Start" => "End" => "Timeout" 순으로 실행됩니다.

    0초라고 해도 web api에 갔다오는 callback 함수 이기 때문에 call stack이 비워진("End"가 찍히고) 다음 실행되기 때문입니다.

    반응형

    'javascript' 카테고리의 다른 글

    Javascript this 관련  (0) 2021.02.23
    [Javascript] undefined 와 null  (0) 2020.12.01

    댓글

Designed by Tistory.