미래 지향적인 웹 앱 구축: The Codest의 전문가 팀이 제공하는 인사이트
The Codest가 최첨단 기술로 확장 가능한 대화형 웹 애플리케이션을 제작하고 모든 플랫폼에서 원활한 사용자 경험을 제공하는 데 탁월한 성능을 발휘하는 방법을 알아보세요. Adobe의 전문성이 어떻게 디지털 혁신과 비즈니스를 촉진하는지 알아보세요...
JavaScript는 단일 스레드 언어이며 동시에 비차단, 비동기 및 동시 언어이기도 합니다. 이 글에서는 그 원리를 설명합니다.
JavaScript 는 컴파일된 언어가 아니라 해석된 언어입니다. 즉, JS를 변환하는 인터프리터가 필요하다는 뜻입니다. 코드 를 기계 코드로 변환합니다. 여러 유형의 인터프리터(엔진이라고도 함)가 있습니다. 가장 널리 사용되는 브라우저 엔진은 V8(Chrome), Quantum(Firefox), WebKit(Safari)입니다. 참고로 V8은 널리 사용되는 비브라우저 런타임에도 사용됩니다, Node.js.
각 엔진에는 메모리 힙, 호출 스택, 이벤트 루프, 콜백 대기열, HTTP 요청, 타이머, 이벤트 등이 포함된 WebAPI가 포함되어 있으며, 모두 JS 코드를 더 빠르고 안전하게 해석하기 위해 자체적인 방식으로 구현되어 있습니다.
기본 JS 런타임 아키텍처. 저자: Alex Zlatkov
단일 스레드 언어는 단일 호출 스택과 단일 메모리 힙을 가진 언어입니다. 즉, 한 번에 한 가지 작업만 실행한다는 뜻입니다.
A 스택
는 실행되는 각 함수에 로컬 컨텍스트를 할당하는 연속적인 메모리 영역입니다.
A 힙
는 훨씬 더 큰 영역으로, 동적으로 할당된 모든 것을 저장합니다.
A 호출 스택
는 기본적으로 프로그램에서 현재 위치를 기록하는 데이터 구조입니다.
간단한 코드를 작성하여 호출 스택에서 어떤 일이 일어나는지 추적해 보겠습니다.
보시다시피 함수는 스택에 추가되고 실행된 후 나중에 삭제됩니다. 이른바 LIFO 방식인 선입선출 방식입니다. 호출 스택에 있는 각 항목을 스택 프레임
.
호출 스택에 대한 지식은 오류 스택 추적을 읽는 데 유용합니다. 일반적으로 오류의 정확한 원인은 첫 번째 줄의 맨 위에 있지만 코드 실행 순서는 상향식입니다.
때때로 다음과 같이 자주 발생하는 오류를 처리할 수 있습니다. 최대 호출 스택 크기 초과
. 재귀를 사용하면 쉽게 얻을 수 있습니다:
함수 foo() {
foo()
}
foo()
를 누르면 브라우저나 단말기가 멈춥니다. 브라우저마다, 심지어 버전마다 호출 스택 크기 제한이 다릅니다. 대부분의 경우 이 정도면 충분하며 다른 곳에서 문제를 찾아야 합니다.
다음은 JS 스레드를 차단하는 예제입니다. 다음과 같이 foo
파일과 바
를 사용하여 노드.js 동기화 함수 readFileSync
.
이것은 반복되는 GIF입니다. 보시다시피 JS 엔진은 첫 번째 호출이 들어올 때까지 기다립니다. readFileSync
가 완료되었습니다. 그러나 이 작업은 foo
파일에 저장되어 있으므로 두 번째 함수는 호출되지 않습니다.
그러나 JS는 논블로킹이 가능하며 멀티스레드처럼 동작할 수도 있습니다. 즉, API 호출, I/O 이벤트 등의 응답을 기다리지 않고 코드 실행을 계속할 수 있습니다. 이는 C++(Chrome) 또는 Rust(Firefox)와 같은 실제 멀티스레딩 언어를 사용하는(내부적으로) JS 엔진 덕분에 가능합니다. 이들은 브라우저 내부에서 웹 API를 제공하거나 ex. Node.js의 I/O API를 제공합니다.
위의 GIF에서 첫 번째 함수가 호출 스택으로 푸시되고 있는 것을 볼 수 있습니다. 안녕하세요
은 콘솔에서 즉시 실행됩니다.
그런 다음 setTimeout
함수를 호출합니다. 호출 스택과 해당 비동기 콜백으로 이동합니다. foo
함수는 웹에이피의 대기열로 이동하여 3초 후에 호출이 발생하도록 설정된 호출을 기다립니다.
그 동안 프로그램은 코드를 계속 진행하며 다음과 같이 표시됩니다. 안녕하세요. 차단되지 않았습니다.
를 클릭합니다.
호출된 후 WebAPI 대기열의 각 함수는 호출이 완료되면 콜백 대기열
. 호출 스택이 비워질 때까지 함수가 대기하는 곳입니다. 호출이 발생하면 함수가 하나씩 이곳으로 이동합니다.
따라서 setTimeout
타이머가 카운트다운을 완료하면 foo
함수는 콜백 큐로 이동하여 호출 스택을 사용할 수 있게 될 때까지 기다렸다가 그곳으로 이동하여 실행되면 다음과 같이 표시됩니다. 비동기 콜백에서 안녕하세요
를 클릭합니다.
문제는 런타임이 호출 스택이 비어 있음을 어떻게 알고 콜백 대기열의 이벤트가 어떻게 호출되는가 하는 것입니다. 이벤트 루프를 만나보세요. 이벤트 루프는 JS 엔진의 일부입니다. 이 프로세스는 호출 스택이 비어 있는지 지속적으로 확인하고, 비어 있는 경우 콜백 대기열에 호출을 기다리는 이벤트가 있는지 모니터링합니다.
이것이 바로 무대 뒤의 모든 마법입니다!
동시성
는 여러 작업을 동시에 실행하지만 동시에 실행하지 않는 것을 의미합니다. 예를 들어 두 개의 작업이 겹치는 시간대에 작동합니다.
병렬 처리
는 두 개 이상의 작업을 동시에 수행하는 것을 의미합니다(예: 여러 계산을 동시에 수행).
스레드
는 서로 독립적으로 실행할 수 있는 코드 실행 시퀀스입니다.
프로세스
는 실행 중인 프로그램의 인스턴스입니다. 프로그램에는 여러 개의 프로세스가 있을 수 있습니다.
In 동기식
프로그래밍을 사용하면 작업이 차례로 실행됩니다. 각 작업은 이전 작업이 완료될 때까지 기다렸다가 그 후에야 실행됩니다.
In 비동기
프로그래밍을 사용하면 한 작업이 실행되면 이전 작업이 완료될 때까지 기다릴 필요 없이 다른 작업으로 전환할 수 있습니다.
단일 스레드와 동기화
: 작업이 차례로 실행됩니다. 각 작업은 이전 작업이 실행될 때까지 기다립니다.
여러 스레드와 동기화
: 작업은 다른 스레드에서 실행되지만 다른 스레드에서 실행 중인 다른 작업을 기다립니다.
단일 스레드를 사용한 비동기
: 다른 작업이 완료될 때까지 기다리지 않고 작업이 실행되기 시작합니다. 주어진 시간에는 하나의 작업만 실행할 수 있습니다.
여러 스레드를 사용한 비동기식
: 작업은 다른 작업이 완료될 때까지 기다리지 않고 다른 스레드에서 실행되어 독립적으로 실행을 완료합니다.
JS 엔진의 내부 작동 방식을 고려하면 JS는 비동기식 단일 스레드 해석 언어로 분류할 수 있습니다. "해석된"이라는 단어는 매우 중요한데, 이는 이 언어가 항상 런타임에 의존적이며 멀티스레딩이 내장된 컴파일된 언어만큼 빠르지 않다는 것을 의미하기 때문입니다.
각 스레드가 별도의 프로세스로 시작되는 경우 Node.js가 실제 멀티 스레딩을 달성 할 수 있다는 점은 주목할 만합니다. 이를 위한 라이브러리가 있지만 Node.js에는 다음과 같은 기능이 내장되어 있습니다. 워커 스레드.
모든 이벤트 루프 GIF는 루페 애플리케이션에서 비동기 시나리오를 테스트할 수 있습니다.
자세히 읽어보세요:
품질을 최우선으로! JavaScript 프로젝트에서 GitHub 워크플로로 코드를 린트하는 5가지 쉬운 단계