저는 JavaScript에 대해 매우 막연한 인상을 받았습니다. 이는 단일 스레드 및 비동기식입니다. 이 기사에서는 주로 JavaScript의 작동 방식에 대해 설명합니다. 하지만 그 전에 먼저 이러한 개념을 이해해 봅시다(지금 배우고 지금 판매하세요) .
프로세스는 시스템 리소스 할당 및 스케줄링의 단위입니다. 실행 중인 프로그램은 프로세스에 해당합니다. 프로세스에는 실행 중인 프로그램과 프로그램에서 사용하는 메모리 및 시스템 리소스가 포함됩니다. 단일 코어 CPU인 경우 동시에 실행되는 프로세스는 하나만 있습니다. 그러나 단일 코어 CPU는 동시에 여러 작업을 실행할 수도 있습니다. 예를 들어 NetEase Youdao Cloud Notes에 블로그 게시물을 작성하는 동안 NetEase Cloud Music의 일일 추천 노래를 들을 수 있습니다. 이는 두 개의 프로세스(다중 프로세스)로 간주됩니다. 실행 메커니즘은 잠시 동안 노래를 재생하고 잠시 동안 입력에 반응하는 것입니다. 그러나 CPU 전환 속도가 매우 빠르기 때문에 전혀 느낄 수 없습니다. 그래서 이 두 프로세스가 동시에 실행되고 있다고 생각하게 됩니다. 리소스는 프로세스 간에 격리됩니다.
스레드란 무엇인가요? 스레드는 프로세스 아래의 실행자입니다. 프로세스는 최소한 하나의 스레드(기본 스레드)를 시작하며 여러 스레드를 시작할 수도 있습니다. 예를 들어 NetEase Cloud Music은 오디오를 재생하는 동시에 가사를 표시합니다. 다중 프로세스의 작업은 실제로 프로세스 내의 스레드를 통해 수행됩니다. 프로세스 아래의 스레드는 리소스를 공유합니다. 여러 스레드가 동시에 동일한 리소스를 운영하면 리소스 경합이 발생합니다. 이것은 또 다른 질문입니다.
병렬성은 여러 작업이 동시에 병렬로 처리되는 프로그램의 실행 상태를 나타냅니다. 하나의 스레드는 동시에 한 가지 작업만 처리할 수 있으므로 병렬 처리에서는 여러 작업을 동시에 수행하려면 여러 스레드가 필요합니다.
동시성이란 여러 가지 작업을 동시에 교대로 처리할 수 있도록 프로그램의 설계 구조를 말합니다. 요점은 한 번에 하나의 작업만 실행된다는 것입니다. 예를 들어 단일 코어 CPU는 여러 작업을 동시에 실행하는 프로세스를 실현할 수 있습니다.
동기화 및 비동기화는 프로그램의 동작을 나타냅니다. 동기(Synchronous)는 프로그램이 호출할 때 결과가 반환될 때까지 기다린다는 의미입니다. 결과가 없을 때까지 반환되지 않습니다. 즉, 동기화는 호출자가 호출 프로세스를 적극적으로 기다리는 것을 의미합니다.
비동기란 호출 후 즉시 반환되지만 결과가 즉시 반환되지 않음을 의미합니다. 호출자는 적극적으로 기다릴 필요가 없습니다. 호출 수신자가 결과를 받으면 호출자에게 적극적으로 알립니다.
예를 들어 밀크티 가게에 가서 음료수를 사세요. 동기화는 고객이 요구사항(요청)을 말한 후 웨이터가 음료를 준비할 때까지 기다리는 것을 의미합니다. 고객은 주문한 음료를 받고 떠난 후 다음 고객이 위의 과정을 계속 반복합니다. 비동기식이란 고객이 먼저 주문을 하기 위해 줄을 섰다가 주문이 완료되면 웨이터가 전화를 걸어 주문을 받는 것을 의미합니다.
그래서 스레드는 동기화 및 비동기성과 직접적인 관련이 없으며 단일 스레드도 비동기성을 달성할 수 있습니다. 구현 방법에 대해서는 아래에서 자세히 설명하겠습니다.
차단 및 비차단은 대기 상태를 나타냅니다. 차단은 호출이 대기하는 동안 스레드가 "일시중단"됨을 의미합니다(CPU 리소스는 다른 곳에 할당됨).
비 차단(Non-blocking)은 대기 프로세스 중에 CPU 리소스가 여전히 스레드에 있고 스레드가 다른 작업도 수행할 수 있음을 의미합니다.
음료를 사기 위해 줄을 서는 것을 예로 들면, Blocking은 기다리는 동안 아무것도 할 수 없다는 뜻이고, Non-Blocking은 기다리는 동안 다른 일을 먼저 할 수 있다는 뜻입니다.
따라서 동기화는 차단 또는 비차단일 수 있고, 비동기식은 차단 또는 비차단일 수 있습니다.
위의 개념을 이해하고 나면 단일 스레드와 비동기 사이에 모순이 없다는 것을 알게 될 것입니다. 그렇다면 JS는 어떻게 실행되나요? JS는 실제로 단일 스레드인지 다중 스레드인지는 특정 운영 환경에 따라 다릅니다. JS는 일반적으로 브라우저에서 실행되며 JS 엔진에 의해 구문 분석되고 실행됩니다. 브라우저를 자세히 살펴보겠습니다.
현재 가장 인기 있는 브라우저는 Chrome, IE, Safari, FireFox, Opera입니다. 브라우저의 핵심은 다중 스레드입니다. 브라우저는 일반적으로 다음과 같은 상주 스레드로 구성됩니다.
렌더링 엔진 스레드: 이름에서 알 수 있듯이 이 스레드는 페이지 렌더링을 담당합니다.
JS 엔진 스레드: JS 구문 분석 및 실행을 담당합니다.
Timed 트리거 스레드: setTimeout, setInterval 등 예정된 이벤트 처리
이벤트 트리거 스레드: DOM 이벤트 처리
비동기 http 요청 스레드: http 요청 처리
렌더링에 유의해야 합니다. 스레드와 JS 엔진 스레드는 동시에 수행될 수 없습니다. 렌더링 스레드가 작업을 실행하는 동안 JS 엔진 스레드는 일시 중지됩니다. JS는 DOM을 조작할 수 있기 때문에 JS가 렌더링 중에 DOM을 처리하면 브라우저가 손실될 수 있습니다.
보통 브라우저에 관해 이야기할 때 렌더링 엔진과 JS 엔진이라는 두 가지 엔진에 대해 이야기합니다. 렌더링 엔진은 페이지를 렌더링하는 방법으로 Chrome/Safari/Opera는 Webkit 엔진을 사용하고 IE는 Trident 엔진을 사용하며 FireFox는 Gecko 엔진을 사용합니다. 서로 다른 엔진이 동일한 스타일을 일관되지 않게 구현하므로 종종 비판을 받는 브라우저 스타일 호환성 문제가 발생합니다. 여기서는 자세히 다루지 않겠습니다.
JS 엔진은 JS 코드의 구문 분석 및 실행을 담당하는 JS 가상 머신이라고 할 수 있습니다. 일반적으로 다음 단계가 포함됩니다.
어휘 분석: 소스 코드를 의미 있는 단어 세그먼트로 분해
구문 분석: 구문 분석기를 사용하여 단어 세그먼트를 구문 트리로 구문 분석
코드 생성: 생성 머신 에너지 실행 중인 코드
코드 실행
다른 브라우저의 JS 엔진도 다릅니다. Chrome은 V8을 사용하고, FireFox는 SpiderMonkey를 사용하고, Safari는 JavaScriptCore를 사용하고, IE는 Chakra를 사용합니다.
JS가 단일 스레드라는 말로 돌아가서, 기본적으로 브라우저는 런타임 중에 JS를 구문 분석하고 실행하기 위해 하나의 JS 엔진 스레드만 엽니다. 그렇다면 왜 엔진이 하나만 있는 걸까요? DOM을 동시에 작동하는 두 개의 스레드가 있으면 브라우저가 다시 손실을 입게 될까요? !
많은 이야기를 나눈 끝에 마침내 JS의 전체 실행 프로세스에 대해 이야기합니다.
먼저 JS 동기 실행 프로세스가 어떻게 달성되는지 살펴보겠습니다. 여기에는 실행 컨텍스트라는 매우 중요한 개념이 포함됩니다. 내 번역 중 하나인 Deep Learning JavaScript Closures에서는 이 개념을 매우 자세히 설명합니다.
실행 컨텍스트는 코드가 실행될 때의 환경을 기록합니다. 현재 실행 상태에는 하나의 실행 컨텍스트가 적용됩니다. 그렇다면 실행 컨텍스트에는 정확히 무엇이 기록됩니까? 아마도 어휘 환경, 가변 환경 등이 있을 것입니다. 간단한 예를 들면 다음과 같습니다.
var x = 10; function foo(){ var y=20; function bar(){ var z=15; } bar(); } foo();
코드가 실행되고 먼저 전역 컨텍스트에 들어갑니다. 그런 다음 foo()
가 실행되면 foo 컨텍스트가 입력됩니다. 물론 이 시점에서도 전역 컨텍스트가 여전히 존재합니다. bar()
를 실행하면 bar 컨텍스트가 다시 입력됩니다. bar()
를 실행한 후 foo 컨텍스트로 돌아갑니다. foo()
를 실행한 후 전역 컨텍스트로 돌아갑니다. 따라서 실행 프로세스의 실행 컨텍스트는 먼저 들어오고 마지막으로 나가는 호출 스택(Call Stack)을 형성합니다. foo()
的时候,就进入了foo上下文,当然此时全局上下文还在。当执行 bar()
的时候,又进入了bar上下文。执行完毕bar()
,回到foo上下文。执行完foo()
,又回到全局上下文。所以,执行过程执行上下文会形成一个调用栈(Call stack),先进后出。
// 进栈 3 bar Context => => 2 foo Context => 2 foo Context 1 global Context 1 global Context 1 global Context // 出栈 3 bar Context 2 foo Context => 2 foo Context => => 1 global Context 1 global Context 1 global Context
在JS执行过程中,有且仅有一个执行上下文在起作用。因为JS是单线程的,一次只能做一件事。
以上的过程都是同步执行的。
我们回顾一下JS中自带了哪些原生的异步事件:
setTimeout
setInterval
事件监听
Ajax请求
etc..
JS异步的效果得益于浏览器的执行环境。实际上,浏览器又开了线程来处理这些BOM事件。举例:
function foo(){ console.log(1); } function bar(){ console.log(2); } foo(); setTimeout(function cb(){ console.log(3); }); bar();
按照上一节的分析,首先进入全局上下文,运行至foo()
,进入了foo上下文环境;执行console.log(1)
,控制台输出1;foo上下文环境出栈,运行至setTimeout
,交给浏览器的定时处理线程;运行至bar()
,进入了bar上下文环境;执行console.log(2)
,控制台输出2;foo上下文环境出栈;等到浏览器线程执行完setTimeout
,返回cb()
回调函数至当前任务队列;当发现执行栈为空时,浏览器的JS引擎会执行一次循环,将事件队列的队首出队至JS执行栈中;执行cb()
,进入cb上下文环境;执行console.log(3)
,控制台输出3;事件队列为空,全局上下文出栈。
以上就是JS引擎的事件循环机制,是实现异步的一种机制。主要涉及到浏览器线程,任务队列以及JS引擎。所以,我们可以看出,JS的异步请求,借助了而它所在的运行环境浏览器来处理并且返回结果。而且,这也解释了为什么那些回调函数的this
指向window
rrreee
rrreee
이전 섹션의 분석에 따르면 먼저 전역 컨텍스트를 입력하고foo()
를 실행한 다음 foo 컨텍스트 환경으로 들어가 console.log(1)를 실행합니다.
, 콘솔은 1을 출력합니다. foo 컨텍스트는 스택에서 꺼내어 setTimeout
으로 실행되고 브라우저의 타이밍 처리 스레드로 전달됩니다. bar()
에 bar 컨텍스트를 입력하고 console.log(2)
를 실행하면 foo 컨텍스트가 팝업될 때까지 기다립니다. setTimeout
실행을 마치고 실행 스택이 발견되면 현재 작업 대기열에 cb()
콜백 함수를 반환합니다. 비어 있으면 브라우저의 JS 엔진 > 이벤트 대기열의 헤드를 JS 실행 스택에서 제거하기 위해 루프가 실행됩니다. cb()
를 실행하여 cb 컨텍스트 환경으로 들어갑니다. ; console.log(3)
를 실행하면 콘솔 출력 3이 비어 있고 전역 컨텍스트가 표시됩니다. this
가 window
를 가리키는 이유도 설명합니다. 왜냐하면 이러한 비동기 코드는 전역 컨텍스트에서 실행되기 때문입니다. 후말: 제가 제대로 이해했는지도 모르겠고, 명확하게 설명했는지도 모르겠습니다. 부적절한 내용이 있으면 지적해 주시기 바랍니다. 위 내용은 JavaScript 동작 메커니즘 및 개념 분석에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!