이 기사는 JavaScript의 실행 메커니즘과 관련된 몇 가지 문제를 소개합니다. 작업이든 인터뷰이든 코드의 실행 순서를 알아야 하는 시나리오가 종종 모든 사람에게 도움이 되기를 바랍니다.
프로세스 및 스레드
우리 모두는 컴퓨터의 핵심이 모든 컴퓨팅 작업을 수행하는 CPU이고 운영 체제가 작업을 담당하는 컴퓨터의 관리자라는 것을 알고 있습니다. 전체 컴퓨터 하드웨어를 관리하는 스케줄링, 리소스 할당 및 관리 응용 프로그램은 특정 기능을 가진 프로그램이며 운영 체제에서 실행됩니다.
Process
프로세스는 데이터 세트에 대해 독립적인 기능을 가진 프로그램의 동적 실행 프로세스로, 운영체제에 의한 자원 할당 및 스케줄링을 위한 독립적인 단위이며, 애플리케이션을 위한 캐리어 프로세스입니다. 러닝(running) 자원을 소유하고 독립적으로 실행할 수 있는 최소 단위이자, 프로그램 실행을 위한 최소 단위이기도 하다.
프로세스의 특징:
프로세스는 프로그램의 실행 프로세스입니다. 일시적이고 수명 주기가 있으며 동적으로 생성되고 종료됩니다.
동시성: 모든 프로세스가 가능합니다.
독립성: 프로세스는 리소스 할당 및 예약을 위한 시스템의 독립적인 단위입니다.
구조적: 프로세스는 프로그램, 데이터 및 프로세스 제어 블록의 세 부분으로 구성됩니다.
Thread
스레드는 프로그램 실행에서 단일 순차 제어 프로세스이며, 프로그램 실행 흐름의 가장 작은 단위이자 프로세서 스케줄링 및 디스패치의 기본 단위입니다. 프로세스는 하나 이상의 스레드를 가질 수 있으며, 각 스레드는 프로그램의 메모리 공간(즉, 프로세스의 메모리 공간)을 공유합니다. 표준 스레드는 스레드 ID, 현재 명령어 포인터(PC), 레지스터 및 스택으로 구성됩니다. 프로세스는 메모리 공간(코드, 데이터, 프로세스 공간, 열린 파일)과 하나 이상의 스레드로 구성됩니다.
프로세스와 스레드의 차이점
스레드는 프로그램 실행의 최소 단위이며, 프로세스는 운영 체제에서 리소스를 할당하는 최소 단위입니다.
프로세스는 하나 이상의 스레드로 구성됩니다. 스레드는 프로세스의 코드입니다. 서로 다른 실행 경로
프로세스는 서로 독립적이지만 동일한 프로세스 아래의 각 스레드는 프로그램의 메모리 공간(코드 세그먼트, 데이터 세트, 힙 등 포함)과 일부 프로세스를 공유합니다. -레벨 리소스(예: 파일 열기 및 신호), 프로세스는 서로 보이지 않습니다.
예약 및 전환: 스레드 컨텍스트 전환은 프로세스 컨텍스트 전환보다 훨씬 빠릅니다.
JS가 단일 스레드인 이유는 무엇인가요?
JavaScript는 처음부터 브라우저 스크립팅 언어로 사용되었습니다. 이는 주로 사용자 상호 작용을 처리하고 DOM을 운영하는 데 사용됩니다. 이는 단일 스레드만 가능하다고 결정합니다. 그렇지 않으면 매우 복잡한 동기화 문제가 발생합니다.
예를 들어 JS가 멀티스레드인 경우 한 스레드는 DOM 요소를 수정하려고 하고 다른 스레드는 DOM 요소를 삭제하려고 하면 브라우저는 누구의 말을 들어야 할지 알 수 없습니다. 따라서 복잡성을 피하기 위해 JavaScript는 처음부터 단일 스레드로 설계되었습니다.
멀티 코어 CPU의 컴퓨팅 성능을 활용하기 위해 HTML5는 JavaScript 스크립트가 여러 스레드를 생성할 수 있도록 허용하지만 하위 스레드는 기본 스레드에 의해 완전히 제어되며 DOM을 작동해서는 안 되는 Web Worker 표준을 제안합니다. . 따라서 이 새로운 표준은 JavaScript의 단일 스레드 특성을 바꾸지 않습니다
브라우저 원칙
프론트엔드 엔지니어로서 브라우저에 익숙해야 하며 브라우저는 다중 프로세스입니다.
브라우저 구성 요소
사용자 인터페이스: 주소 표시줄, 앞으로/뒤로/새로 고침/북마크 포함
브라우저 엔진: 사용자 인터페이스와 렌더링 엔진 간에 지침을 전송
렌더링 엔진: 요청된 콘텐츠를 그리는 데 사용됩니다
네트워크: http 요청과 같은 네트워크 호출을 완료하는 데 사용되며 플랫폼 독립적인 인터페이스를 가지며 다양한 플랫폼에서 작동할 수 있습니다
JavaScript 해석기: JavaScript 코드를 구문 분석하고 실행하는 데 사용됩니다.
사용자 인터페이스 백엔드: 콤보 상자 및 창과 같은 기본 위젯을 그리는 데 사용되며, 맨 아래 레이어는 운영 체제의 사용자 인터페이스를 사용합니다.
데이터 저장소: 지속성 레이어에 속하며 브라우저가 이를 저장합니다. 쿠키와 유사한 다양한 데이터에 대해 HTML5는 가볍고 완전한 클라이언트 측 저장 기술인 웹 데이터베이스 기술을 정의합니다
참고: 대부분의 브라우저와 달리 Google(Chrome) 브라우저의 각 탭 페이지는 렌더링 엔진 인스턴스. 각 탭은 독립적인 프로세스입니다
브라우저에는 어떤 프로세스가 포함되어 있나요
브라우저 프로세스
브라우저의 주요 프로세스(조정 및 제어 담당)에는 하나의 프로세스만 있습니다
브라우저 인터페이스 표시 및 사용자와의 상호 작용을 담당합니다. 정방향, 역방향 등
각 페이지의 관리, 다른 프로세스 생성 및 소멸을 담당
렌더링(Renderer) 프로세스를 통해 얻은 메모리에 있는 비트맵(bitmap)을 사용자 인터페이스에 그립니다.
네트워크 리소스, 다운로드 등 관리
타사 플러그인 프로세스
타사 플러그인 관리를 담당
GPU 프로세스
3D 렌더링 및 하드웨어 가속(최대 1개)
렌더링 프로세스
페이지 문서 구문 분석, 실행 및 렌더링을 담당
렌더링 프로세스에 포함되는 스레드
GUI 렌더링 스레드
주로 담당 HTML, CSS, DOM 트리 작성, 레이아웃, 그리기 등을 구문 분석합니다.
이 스레드는 JavaScript 엔진과 관련이 있습니다. 스레드는 상호 배타적입니다. JavaScript 엔진 스레드가 실행되면 작업 대기열이 일시 중지됩니다. 유휴 상태이면 메인 스레드가 GUI 렌더링을 실행합니다
JavaScript 엔진 스레드
는 주로 JavaScript 스크립트 처리 및 코드 실행을 담당합니다(예: V8 엔진)
브라우저에는 JS를 실행하는 JS 엔진 스레드가 하나만 있을 수 있습니다. 즉, JS는 단일 스레드입니다
JS 엔진 스레드와 GUI 렌더링 스레드는 상호 배타적이므로 JS 엔진은 페이지 렌더링을 차단합니다
Timed Trigger 스레드
가 담당합니다. 타이머 기능 실행 (setTimeout, setInterval)
브라우저 타이밍 카운터는 JS 엔진에서 계산되지 않습니다. (JS는 싱글 스레드이기 때문에 차단되면 카운터의 정확도에 영향을 미칩니다.)
시간 및 트리거 별도의 스레드를 통한 타이밍(타이밍이 완료된 후 이벤트 트리거 스레드의 이벤트 큐에 추가되고 JS 엔진이 유휴 상태가 된 후 실행을 기다림) 이 스레드는 타이머 스레드라고도 불리는 타이밍 트리거 스레드입니다
W3C HTML 표준에는 setTimeout에서 4ms 미만의 시간 간격이 4ms로 계산된다고 규정되어 있습니다
이벤트 트리거 스레드
준비된 이벤트를 JS 엔진 스레드에 전달하여 실행을 담당합니다
이벤트가 발생하면 이 스레드가 트리거되면 해당 이벤트가 처리될 대기열의 끝에 추가되어 JS 엔진 처리를 기다립니다
비동기 요청 스레드
XMLHttpRequest 연결 후 브라우저는 스레드를 엽니다
상태 변경 요청, 해당 콜백 함수가 있는 경우 비동기 요청 스레드는 상태 변경 이벤트를 생성하고 해당 콜백 함수를 대기열에 넣어 JS 엔진이 실행될 때까지 기다립니다
동기화 및 비동기
JavaScript는 단일 스레드이기 때문에 작업이 동기 작업만 될 수는 없다고 판단합니다. 시간이 오래 걸리는 작업도 동기 작업으로 실행하면 페이지 차단이 발생합니다. 따라서 JavaScript 작업은 일반적으로 두 가지 범주로 나뉩니다.
동기 작업
동기 작업은 메인 스레드에서 실행 대기 중인 작업의 경우 이전 작업이 실행된 후에만 다음 작업을 실행할 수 있습니다.
비동기 작업
비동기 작업은 다음을 참조합니다. 메인 스레드에 들어가지 않고 "작업 큐"(이벤트 큐) 작업에 들어가는 경우, "작업 큐"가 비동기 작업이 실행될 수 있음을 메인 스레드에 알릴 때만 작업이 실행을 위해 메인 스레드에 들어갑니다.
일반적인 비동기 작업: 타이머, ajax, 이벤트 바인딩, 콜백 함수, 약속, 비동기 대기 등
동기 및 비동기 작업은 각각 서로 다른 실행 "장소"에 들어가고 동기적으로 메인 스레드에 들어가고 비동기적으로 이벤트 테이블에 들어갑니다. 그리고 기능을 등록해 보세요.
이벤트 테이블에 지정된 사항이 완료되면 이 기능은 이벤트 큐로 이동됩니다.
메인 스레드의 작업이 실행된 후에는 이벤트 큐로 이동하여 해당 함수를 읽고 실행을 위해 메인 스레드에 들어갑니다.
위의 과정이 계속 반복되는데, 이를 흔히 이벤트 루프라고 합니다.
메인 스레드 실행 스택이 비어 있는지 어떻게 알 수 있는지 묻지 않을 수 없습니다. js 엔진에는 메인 스레드 실행 스택이 비어 있는지 지속적으로 확인하는 모니터링 프로세스가 있습니다. 일단 비어 있으면 이벤트 큐로 이동하여 호출 대기 중인 함수가 있는지 확인합니다.
매크로 작업 및 마이크로 작업
광의의 동기 작업 및 비동기 작업 외에도 JavaScript에는 더 자세한 작업 정의도 있습니다.
매크로 작업: 전역 코드, setTimeout, setInterval
Micro-task: new Promise().then(callback) process.nextTick()
다양한 유형의 작업이 서로 다른 작업 대기열에 들어갑니다.
이벤트 루프의 순서에 따라 js 코드의 실행 순서. 전체 코드(매크로 태스크)를 입력하면 첫 번째 사이클이 시작됩니다. 그런 다음 모든 마이크로태스크를 실행합니다. 그런 다음 매크로 작업을 다시 시작하고 실행할 작업 대기열 중 하나를 찾은 다음 모든 마이크로 작업을 실행합니다.
실행 스택 및 작업 대기열
실행 스택
JavaScript 코드는 실행 컨텍스트에서 실행됩니다. JavaScript에는 세 가지 실행 컨텍스트가 있습니다.
전역 실행 컨텍스트
JS 함수가 호출되면 함수 실행 컨텍스트가 생성됩니다
eval 실행 컨텍스트, eval 함수에 의해 생성된 컨텍스트(덜 사용됨)
일반적으로 JS 코드에는 둘 이상의 컨텍스트가 있는데 이러한 컨텍스트의 실행 순서는 무엇입니까?
우리 모두는 스택이 후입선출 데이터 구조라는 것을 알고 있습니다. JavaScript의 실행 스택은 JS 엔진이 코드를 실행할 때 전역 컨텍스트가 생성되어 푸시됩니다. 실행 스택. 함수 호출이 발생할 때마다 함수 실행 컨텍스트가 생성되어 실행 스택으로 푸시됩니다. 엔진은 스택 상단부터 함수 실행을 시작하고, 실행 후 실행 컨텍스트가 나타납니다.
function add(){ console.log(1) foo() console.log(3) } function foo(){ console.log(2) } add()
위 코드의 실행 스택을 살펴보겠습니다.
Task queue
앞서 JavaScript의 모든 작업은 동기 작업과 비동기 작업, 동기 작업으로 나누어진다고 말씀드렸습니다. 이름에서 알 수 있듯이 즉시 실행되는 작업은 일반적으로 실행을 위해 메인 스레드에 직접 들어갑니다. 우리의 비동기 작업은 작업 대기열에 들어가서 실행하기 전에 메인 스레드의 작업이 실행될 때까지 기다립니다.
작업 대기열은 관련 비동기 작업이 실행 스택에 들어갈 수 있음을 나타내는 이벤트 대기열입니다. 기본 스레드는 작업 대기열을 읽어 그 안에 있는 이벤트를 읽습니다.
큐는 선입선출(FIFO) 데이터 구조입니다.
위에서 비동기 작업이 매크로 작업과 마이크로 작업으로 나눌 수 있다고 언급했으므로 작업 대기열도 매크로 작업 대기열과 마이크로 작업 대기열로 나눌 수 있습니다.
매크로 작업 대기열: 상대적으로 대규모 작업의 경우 일반적인 대기열은 다음과 같습니다. setTimeout, setInterval, 사용자 상호 작용, UI 렌더링 등
Microtask 대기열: 소규모 작업을 수행하며 일반적인 작업에는 Promise, Process.nextTick이 포함됩니다.
Event-Loop
실행을 위해 메인 스레드에 배치되고 비동기 작업(클릭 이벤트, 타이머, ajax 등)이 백그라운드에서 실행되어 I/O 이벤트가 완료되거나 동작 이벤트가 트리거될 때까지 기다립니다.
시스템은 백그라운드에서 비동기 작업을 실행합니다. 비동기 작업 이벤트(또는 동작 이벤트가 트리거되면) 작업은 작업 대기열에 추가되고 각 작업은 콜백 함수에 의해 처리됩니다.
여기서 비동기 작업은 매크로 작업과 마이크로 작업으로 구분됩니다. 매크로 작업은 매크로 작업 대기열에 들어가고 마이크로 작업은 마이크로 작업 대기열에 들어갑니다.
실행 작업 대기열의 작업은 실행 스택에서 구체적으로 완료됩니다. 메인 스레드의 모든 작업이 실행되면 마이크로 작업 대기열을 읽어서 모두 실행합니다. 매크로 작업을 읽어보세요. Queue
위의 프로세스가 계속 반복되는데, 이를 우리가 흔히 이벤트 루프(Event-Loop)라고 부릅니다.
질문 검증 예시
검증할 질문을 살펴보겠습니다
(async ()=>{ console.log(1) setTimeout(() => { console.log('setTimeout1') }, 0); function foo (){ return new Promise((res,rej) => { console.log(2) res(3) }) } new Promise((resolve,reject)=>{ console.log(4) resolve() console.log(5) }).then(()=> { console.log('6') }) const res = await foo(); console.log(res); console.log('7') setTimeout(_ => console.log('setTimeout2')) })()
인쇄 순서는 1,4,5,2,6,3,7,setTimeout1,setTimeout2
분석:
코드 맨 위에서 시작 다음 실행, 먼저 console.log(1)를 만나고 1을 직접 인쇄한 다음 매크로 작업에 속하는 타이머를 만나 매크로 작업 대기열에 넣습니다.
그런 다음 약속을 만나세요. 왜냐하면 new Promise는 동기 작업이므로 4를 직접 인쇄합니다. 후속 then 함수인 해결을 만나면 이를 마이크로태스크 대기열에 넣고 5
를 인쇄한 다음 wait foo를 실행합니다. foo 함수에 약속이 있고 새 약속은 다음과 같습니다. 동기식 작업이므로 2를 직접 인쇄하고 Wait Return은 약속 콜백이며, Wait 이후의 작업은 마이크로태스크 대기열에 넣습니다
마지막으로 타이머가 발견되어 매크로태스크 대기열에 넣습니다
실행 스택 작업이 완료되었습니다. , 먼저 마이크로태스크 실행을 얻기 위해 마이크로태스크 대기열로 이동하고 먼저 실행합니다. 첫 번째 마이크로태스크의 경우 6을 인쇄한 다음 두 번째 마이크로태스크를 실행하여 3, 7을 인쇄합니다
마이크로태스크가 실행된 후 매크로태스크 대기열로 이동하여 매크로태스크 실행, 인쇄 setTimeout1, setTimeout2
[관련 권장 사항: javascript 학습 튜토리얼]
위 내용은 기술적인 답변: JavaScript 실행 메커니즘의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!