자바스크립트의 스레드 (#Event Loop)
❓ 프로세스(process)란?
프로세스(process)란 단순히 실행 중인 프로그램(program)이라고 할 수 있다. 즉, 사용자가 작성한 프로그램이 운영체제에 의해메모리 공간을 할당받아 실행 중인 것
을 말한다. 이러한 프로세스는 프로그램에 사용되는데이터
와 메모리 등의자원
그리고스레드
로 구성된다.❓ 스레드(thread)란?
스레드(thread)란 프로세스(process) 내에서 실제로 작업을 수행하는주체
를 의미한다. 모든 프로세스에는 한 개 이상의 스레드가 존재하여 작업을 수행한다. 또한, 두 개 이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스(multi-threaded process)라고 한다.
자바스크립트의 가장 큰 특징 중 하나는 단일 스레드(Single thread) 기반 언어이며, 동기적 언어 이다. 보통 싱글 쓰레드라고 하면 한 번에 하나의 작업만 수행할 수 있다고 생각한다. 그렇다면 자바스크립트를 주로 사용하는 웹 사이트에서는 어떻게 한번에 여러 요청을 받을까?
1. 개요
(1) 자바스크립트는 정말 싱글 쓰레드일까?
맞다. 정확하게 말하면 자바스크립트의 메인 쓰레드인 이벤트 루프
가 싱글 쓰레드이기 때문에 자바스크립트를 싱글 쓰레드 언어라고 부른다. 하지만 이벤트 루프만 독립적으로 실행되지 않고 웹 브라우저나 NodeJS같은 멀티 쓰레드 환경에서 실행된다.
즉, 자바스크립트 자체는 싱글 쓰레드가 맞지만 자바스크립트 런타임은 싱글 쓰레드가 아니다.
(2) 자바스크립트가 싱글 스레드라면...
console.log("1");
setTimeout(console.log, 5000, "2"); // 5초 후, console.log 함수 실행
console.log("3");
// [출력]
// 1
// 3
// 2
❓ 왜 출력값이 1, 2, 3이 아닐까?
setTimeout(...)는 5초 후에 콘솔창에 '2'를 출력할 것을 요구하고 있다. 싱글 스레드라면 setTimeout(...) 함수가 끝난 후 다음 코드를 실행해야 할 것 같다. 하지만 '3'이 먼저 출력되었다. (0ms로 해도 결과는 동일함. setTimeout에 지연이 존재)
📌 기존의 동기식 요청
기존 동기식 요청은 코드를 한줄 한줄 차례대로 실행한다. 그래서 하나의 작업에 걸리는 시간에 관계 없이 첫 번째 코드가 실행 된 뒤 다음 코드가 실행된다. 이렇게 되면 앞의 작업시간이 길수록 시간 및 자원의 낭비가 심해진다.
하나의 요청이 완료될 때 까지 기다리지 않고 동시에 다른 작업을 실행하는 비동기 호출로 극복할 수 있다.
그렇다면 자바스크립트는 싱글 쓰레드로 동작하며 어떻게 한번에 여러 요청을 처리할까? 바로 비동기 작업을 통해 여러 요청들을 처리하게 된다. 그렇다면 비동기 작업은 어떻게 동작할까?
이런 현상을 이해하기 위해서는 이벤트 루프에 대해 알 필요가 있다.
(3) Event Loop와 용어 정리
- JS Engine의 구성
자바스크립트 엔진은 Memory Heap
과 Call Stack
으로 구성되어 있다. 가장 유명한 것이 구글의 V8 Engine이다. 자바스크립트는 단일 스레드 (sigle thread) 프로그래밍 언어인데, 이 의미는 Call Stack이 하나 라는 의미이다.
엔진(v8) 요청이 들어올 때마다 해당 요청을 순차적으로 호출 스택에 담아 처리할 뿐이다. 그렇다면 비동기 요청은 어떻게 이루어지며, 동시성에 대한 처리는 누가 하는 걸까? 바로 이 자바스크립트 엔진을 구동하는 환경, 즉 브라우저나 Node.js가 담당한다. (거듭 말하지만 자바스크립트 엔진은 싱글스레드인데, 이를 구동하는 환경, 즉 브라우저 엔진과 Node.js가 멀티 스레드 환경이다.)
브라우저 엔진 : 크롬과 같은 웹 브라우저 엔진으로, 다양한 작업을 수행
호출 스택(Call Stack) : 자바스크립트 에서는 수행해야 할 함수를 순차적으로 호출 스택에 담아 처리함
Memory Heap : 메모리 할당이 일어나는 곳 (ex, 선언한 변수, 함수 등이 담겨져 있음)
Call Stack : 코드가 실행될 때 쌓이는 곳. stack 형태로 쌓임.
- Web APIs
Web API 는 브라우저에서 제공하는 API 로, DOM
, Ajax
, Timeout
등이 있다. Call Stack에서 실행된 비동기 함수는 Web API를 호출하고, Web API는 콜백함수를 Callback Queue
에 담아둔다.(promise, callback, clickevent etc..)
- Callback Queue
비동기적으로 실행된 콜백함수가 보관 되는 영역이다. Task Queue
, Microtask Queue
등이 존재 한다.(setTimeout, promise 등 다른 큐에 추가된다)
- Event loop
Event Loop는 Call Stack과 Callback Queue의 상태를 체크하여, Call Stack이 빈 상태가 되면, Callback Queue의 첫번째 콜백을 Call Stack에 추가한다.
2. 이벤트 루프(Event Loop)와 동시성(Concurrency)
1 - (2)
의 코드가 어떻게 처리되는 지 천천히 살펴보자.
line 1 : console.log("1") 코드 수행
1) Call Stack에 console.log("1") 추가
2) console.log("1") 실행, 콘솔창에 '1'이 출력
3) 실행 완료 후 Call Stack에서 console.log("1") 제거
line 2 : setTimeout(console.log, 5000, "2") 코드 수행
1) Call Stack에 setTimeout(...)
추가
2) setTimeout(...) 실행, Web API의 timer 스레드
에 작업을 넘김 (앞서 말한 TimeOut)
⇒ 즉, 5초 간 기다리는 작업을 timer 스레드
라는 서브 스레드에서 수행하는 것이다. 이 덕분에 메인 스레드에서는 5초 간의 블로킹이 일어나지 않고 다음 코드를 수행할 수 있다. 자바스크립트가 호출 스택이 하나인 싱글 스레드임에도 여러 가지 일을 동시에 처리하는 것처럼 동작할 수 있는 이유이다. 이를 동시성(Concurrency) 라고 표현한다.
3) 실행 완료된 setTimeout(...)는 Call Stack에서 사라진다.
동시성(Concurrency) : 여러 작업이 마치 동시에 일어나는 것처럼 보이는 것
line 3 : console.log("3") 코드 수행
1) Call Stack에 console.log("3")
적재
2) console.log("3") 실행, 콘솔창에 '3' 출력
3) 실행이 완료된 console.log("3")
는 Call Stack에서 제거
5초 뒤, Web API의 timer 스레드는 console.log("2")를 Callback Queue로 옮김
(엄밀히 말하면 5초의 대기를 보장하는 것이지, 정확히 5초 뒤에 실행되는 것은 아님)
1) 이벤트 루프가 Callback Queue에 있던 console.log("2")를 Call Stack에 추가
⇒ 이벤트 루프(Event Loop)는 항상 루프를 돌면서 Call Stack과 CallBack Queue를 확인한다. 그러다 Call Stack이 빈 상태가 되면, Callback Queue에 있는 함수를 하나씩 Call Stack으로 올려주는 것이다.
2) Call Stack에 올라간 함수가 실행되어, 콘솔창에 '2' 출력
3) 실행이 완료된 console.log("2") 는 Call Stack에서 제거
⇒ 자바스크립트는 싱글 스레드이지만 이벤트 루프, Web APIs(노드의 경우 C++ APIs), Callback Queue가 존재하기 때문에 비동기 콜백 작업이 가능하다.
3. Callback Queue도 종류가 있다
앞서 Callback Queue는 비동기적으로 실행된 콜백함수가 보관 되는 영역이며, Task Queue, Microtask Queue 등이 존재 한다고 했다. 이 말은, setTimeout, promise 등이 다른 Queue에 추가된다는 것이다.
다음 코드를 바탕으로 처리 과정을 알아보자.
console.log("script start");
setTimeout(function() {
console.log("setTimeout");
}, 0);
Promise.resolve().then(function() {
console.log("promise1");
}).then(function() {
console.log("promise2");
});
console.log("script end");
위의 코드를 실행하면 다음과 같은 결과 화면을 얻을 수 있다.
script start
script end
promise1
promise2
setTimeout
Promise, setTimeout 순으로 실행되는 이유는, Promise가 Microtask 이기 때문이다. Microtask는 쉽게 말해 일반 태스크보다 더 높은 우선순위를 갖는 태스크라고 할 수 있다. 즉, 태스크 큐에 대기중인 태스크가 있더라도 마이크로 태스크가 먼저 실행된다.
즉, Promise의 then()의 콜백 은 Task Queue가 아닌 Microtask Queue에 담긴다.
출처
자바스크립트 이벤트 루프
자바스크립트는 왜 싱글 쓰레드일까?
자바스크립트는 정말 싱글스레드일까?
스레드의 개념
Author And Source
이 문제에 관하여(자바스크립트의 스레드 (#Event Loop)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@keinn51/자바스크립트의-스레드-Event-Loop저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)