async-1(event loop & blocking & non-blocking & concurrency & process async task)

GI JUNG·2023년 1월 22일
5

javascript

목록 보기
7/12
post-thumbnail

promise에 대해서는 async await만 알고 넘어갔지만 Promise를 다루고 then, catch 등 스스로 직접 코드를 바꿔서 짜 보려고 하니 못 짰다. 사실상 promise는 시간이 오래 걸리는 작업에 대해서 동기적으로 처리하고 싶어서 쓴다는 사실 밖에 몰랐으며 자세하게 어떻게 이루어지는 알지 못해서 이번 포스트를 통해서 확실히 알아가고자 한다.

🍀 Single Thread

javascript는 single thread(하나의 call stack만 존재함을 의미) language라고 한다. multi thread와 같이 병렬적으로 여러 작업을 처리하는 것과 달리 한 번에 한 작업만을 처리할 수 있다. 여기서 single thread는 한다. 또한, 하나의 작업을 처리하는 도중에 다른 작업을 수행할 수 있고 처리하는 작업을 마쳐야 다음 작업을 넘어갈 수 있다.

아래 예시를 보자

console.log("a");
console.log("b");

"b"를 출력하는 코드는 "a"를 출력한 뒤에 실행이 가능하다.

console.log("a");
while(true){}

console.log("b");

while구문의 처리가 영원히 끝나지 않으므로 "b"를 출력하는 코드는 영원히 실행되지 않는다.

위의 두 가지 예시를 보면 single thread는 크게 두 가지 특징을 가진다.
1. 한 번에 하나의 작업만을 처리할 수 있다.
2. 도중에 다른 작업이 끼어들 수 없으며 처리하던 작업이 완전히 끝나야 다른 작업을 실행할 수 있다.

그리고 mdn에서 찾아보면 javascript는 "Run-to-completion"이라 한다. 다른 메시지의 처리를 시작하기 전에 진행 중이던 메세지는 완전히 끝남을 보장한다.

Each message is processed completely before any other message is processed.

This offers some nice properties when reasoning about your program, including the fact that whenever a function runs, it cannot be preempted and will run entirely before any other code runs (and can modify data the function manipulates). This differs from C, for instance, where if a function runs in a thread, it may be stopped at any point by the runtime system to run some other code in another thread.

🤔🤔🤔 여기서 javascript가 single thread임을 안 순간 들었던 생각은 single thread인데 내가 setTimeout 또는 setInterval로 작업을 동시에 수행하지 않았나??? 생각이 들었다. 그래서 그럼 Multi Thread가 맞지 않나?? 라는 생각을 했다. 당장 코드를 짜기에 급급한 공부방법에 반성을 하게 된 시간이다.....

결론은 javascript는 single thread가 맞으며 setTimeout과 같은 비동기 처리는 javascript engine을 구동하는 런타임 환경(browser)에서 ansychronous, non-blocking 등의 작업들을 담당하여 동시성을 보장한다.

javascript 런타임에서 지원을 하나 헷갈릴 수 있는데 javascript 런타임 자체에서 비동기를 지원하는 것이 아닌 browser(javascript engine runtime environment)에서 비동기와 관련된 작업들을 처리한다.

그럼 여기서 어떻게 비동기 처리를 지원하는가??? 생각이 들 수 있다.
이것에 대해서는 javascript가 동작하는 원리를 짚고 넘어갈 필요가 있다.

🍀 How does js work?

javascript가 비동기를 지원하여 동시성을 보장할 수 있는 것은 javascript가 이벤트 루프 기반으로 동작하기 때문이다.
이 말은 실행되는 코드(작업)를 call stack에서 LIFO방식으로 처리하며 비동기 작업을 Web API에서 맡고 있다가 call stack이 비었을 때 callback queue에서 대기 중이던 작업을 call stack으로 올려 그 때 javascript runtime에서 실행한다.

call stack: 코드가 호출되면서 LIFO방식으로 처리되는 영역

Web API: 웹 브라우저에서 제공하는 자체 API로서 해당 비동기 작업을 처리한다. Ex) DOM(document), HttpRequest, setTImeout & setInterval

callback queue: Task Queue라고도 하며 비동기 작업이 처리 된 후 Web API에서 넘겨 받은 callback function들을 저장한다. 이 때 저장되는 순서는 FIFO로 저장된다.
callback queue는 하나의 큐로 이루어져 있지 않고, Microtask Queue, Animation Queue로 이루어져 있다고 한다.

event loop: call stack과 callback queue를 계속 모니터링 하며 call stack이 비어있을 때 callback queue에 처리할 callback이 있다면 call stack에 넣어준다.

아래는 event loop 기반으로 하는 javascript를 도식화하면 아래 그림과 같다. 👇

javascript runtime architecture
-> javascript engine은 call stack과 heap memory로 구성되어있다.

Event loop

event loop가 어떻게 진행되는 더 알고가보자면 아래 코드와 같다.

while(queue.waitForMessage()){
  queue.processNextMessage();
}

현재 처리할 수 있는 메시지가 존재하지 않으면 새로운 메시지가 도착할 때까지 동기적으로 대기한다라고 mdn에서 찾아볼 수 있다.. 즉, 비동기에서 처리할 callback function이 없으면 callback function이 들어올 때까지 계속 모니터링 하다가 queue에 처리할 것이 있다면 처리한다는 것이다.

🚀 javascript에서 비동기를 처리하는 과정

위에서 event loop 기반으로 비동기를 처리한다는 것을 언급했다. 하지만, 글로 읽는 것 보다는 어떻게 진행되는지 직접 해보는 것이 좋다. 아래 예시를 보자.

console.log('start of file'); // 1️⃣

setTimeout(function timeout() { // 2️⃣
    console.log("after 2 seconds!!");
}, 2000);

console.log("end of file"); // 3️⃣

blocking

javascript가 blocking(차단) 언어라면 1️⃣ -> 2️⃣ -> 3️⃣순서로 진행이 될 것이다. 즉, 2️⃣에서의 코드는 2초 후에 timeout이라는 callback function을 실행하게 되므로 약 2초 이상의 시간이 소요된 후 3️⃣의 코드가 실행된다. 이처럼 blocking이란 순서대로 동기적으로 실행되어 3️⃣의 코드의 실행이 2️⃣의 코드 실행이 끝날 때까지 차단된다는 것이다. 3️⃣의 코드가 차단된 다는 것은 2️⃣에 대한 작업이 call stack에 머물고 있다는 의미이기도 하다.

하지만, 아래 결과를 보면 javascript가 blocking 언어가 아님을 알 수 있다.

non-blocking

위를 통해 javascript는 non-blocking언어임은 알겠는데 setTimeout과 같은 비동기 코드를 non-blocking으로 처리할 수 있을까란 의문이 든다. 이는 비동기 작업을들 Web API에게 넘겨 다음 코드들이 실행 될 수 있게 한다.

코드 실행 순서

  1. javascript가 실행되면 1️⃣에 해당하는 작업이 call stack에 쌓인 후 처리된다.

  2. 2️⃣에 해당하는 setTimeout은 비동기처리가 필요한 작업으로 javascript engineweb API에게 작업을 위임을 하며 위임한 순간 부터 2초를 카운트 한다.

  3. 3️⃣에 해당하는 작업이 call stack에 쌓인 후 처리된다.

  4. 2초가 지나면 callback queue에 callback function이 들어간다.

  5. callback queue에 들어온 callback function은 call stack이 비었다면 event loop에 의해 call stack으로 넣어지고 처리된다.

코드가 실행되는 순서를 보면 비동기 작업인 setTimeout을 기다리지 않고 3️⃣이 처리된다. 즉, 2️⃣에 의해 3️⃣의 작업이 차단되지 않았다. 이를 non-blocking(비차단)이라고 하며 비동기 작업을 자체 thread가 있는 Web API에게 넘겨 처리하게 하면서 call stack에 있는 작업을 동시에 처리할 수 있게 된다. 이를 concurrency(동시성)이라한다. 이로 인해 javascript를 non-blocking language(비차단 언어)ensure concurrency(동시성 보장)이 된다.

🔥 마치며

이번에 공부하면서 javascript는 대체 뭘까? 생각이 든다....ㅋㅋㅋㅋ javascript 자체는 비동기 처리를 지원하지 않으나 browser의 web api의 도움으로 비동기 언어를 구사할 수 있으며, non-blocking, single thread, ensure concurrency를 내포하고 있다. 원래는 promise를 공부하려고 시작했으나 계속 파다 보니까 이 부분부터 알아야 promise의 내용들을 더 완벽하게 알 수 있을 것 같아서 정리를 해 보았다. 이제는 "이것"을 공부해봐야지 하면 관련된 내용들이 우수수 떨어지는 것에 익숙하다.... 급하게 블로깅 할 바엔 미래의 나를 위해서 그럴 수 없다. 개발을 공부하다 보면 분명 했었는데 얼마 지나지 않아서 까먹기 일수이기 때문이다🥲. promise를 공부하게 되면서 javascript와 더욱 더 친숙해진 느낌이지만 "javascript는 왜 빠를 것 같은 multi thread를 택하지 않고 single thread를 선택"했을까 의문이 남았는데 이는 javascript는 web에서 쓰이는 언어로 server와의 통신이 빈번하게 일어나 multi thread 환경에서 일어나는 단점인 교착 상태와 충돌 등 복잡한 시나리오를 처리할 필요가 없어 구현하기 쉬운 단일 스레드 언어를 택했다고 한다.

📚 참고

how javascript work1??
how javascript work2???
mdn event loop
why javascript is single thread
single thread VS multi thread
event loop visualization

profile
step by step

0개의 댓글