node.js의 비동기 프로그래밍 : 이벤트 루프에 대한 심층적 인 이해
비동기 프로그래밍은 모든 프로그래밍 언어에서 매우 어려운 일입니다. 동시성, 병렬 처리 및 교착 상태와 같은 개념은 가장 숙련 된 엔지니어조차 까다 롭습니다. 비동기로 실행 된 코드는 버그가있을 때 예측하기 어렵고 추적하기가 어렵습니다. 그러나 현대 컴퓨팅에는 다중 코어 프로세서가 있기 때문에이 문제는 불가피합니다. 각 CPU 코어에는 자체 열 제한이 있으며 단일 코어 성능 개선은 병목 현상에 도달했습니다. 이를 통해 개발자는 효율적인 코드를 작성하고 하드웨어 리소스를 최대한 활용하도록 유도합니다.
JavaScript는 단일 스레드이지만 Node.js가 현대 아키텍처를 활용할 수있는 능력을 제한합니까? 가장 큰 과제 중 하나는 멀티 스레딩의 고유 한 복잡성을 다루는 것입니다. 새 스레드 생성 및 스레드간에 컨텍스트 전환을 관리하는 데 비용이 많이 듭니다. 운영 체제와 프로그래머는 수많은 에지 케이스를 처리하는 솔루션을 제공하기 위해 많은 노력이 필요합니다. 이 기사는 Node.js가 이벤트 루프를 통해이 문제를 해결하는 방법을 설명하고 Node.js 이벤트 루프의 다양한 측면을 탐색하며 작동 방식을 보여줍니다. 이벤트 루프는 Node.js의 킬러 기능 중 하나입니다.이 까다로운 문제를 완전히 새로운 방식으로 해결하기 때문입니다.
키 포인트
이벤트 루프는 반 인간이므로 콜 스택 또는 콜백 큐가 비어 있으면 종료 할 수 있습니다. 이 루프는 들어오는 연결로부터 콜백을 얻기 위해 운영 체제를 폴링해야합니다.
이벤트 루프는 여러 단계로 실행됩니다 : 타임 스탬프 업데이트, 루프 활동 검사, 타이머 실행, 보류중인 콜백 실행, 유휴 처리기 실행, 설정 콜백 실행 준비, 폴링 시간 초과 계산, 차단, 콜백 실행, 닫기 확인 콜백 실행 및 종료 반복. -
각 단계의 끝에서 루프는 프로세스를 실행합니다 .nextTick () 콜백은 각 단계의 끝에서 실행되기 때문에 이벤트 루프의 일부가 아닙니다. setimmediate () 콜백은 전체 이벤트 루프의 일부이므로 이름에서 알 수 있듯이 즉시 실행되지 않습니다. 일반적으로 setimmediate ()를 사용하는 것이 좋습니다.
-
이벤트 루프 란 무엇입니까? -
이벤트 루프는 단일 스레드, 비 블로킹 및 비동기 동시 루프입니다. 컴퓨터 과학 학위가없는 사람의 경우 데이터베이스 조회를 수행하는 웹 요청을 상상해보십시오. 단일 스레드는 한 번에 하나의 작업 만 수행 할 수 있습니다. 데이터베이스가 응답하기를 기다리는 대신 큐의 다른 작업을 계속 처리합니다. 이벤트 루프에서 메인 루프는 통화 스택을 확장하고 콜백을 기다리지 않습니다. 루프가 차단되지 않으므로 여러 웹 요청을 동시에 처리 할 수 있습니다. 여러 요청이 동시에 대기되어 동시에 대상이 될 수 있습니다. 루프는 요청의 모든 작업이 완료되기를 기다리지 않지만 콜백이 차단되지 않고 발생하는 순서에 따라 처리됩니다.
루프 자체는 반 인테 나이트이므로 통화 스택 또는 콜백 큐가 비어 있으면 루프를 종료 할 수 있습니다. 통화 스택은 Console.log와 같은 동기 코드로 간주 될 수 있으며, 더 많은 작업을 설문 조사하기 전에 확장하기 전에 확장됩니다. Node.js는 기본 Libuv를 사용하여 수신 연결에서 콜백을 위해 운영 체제를 투표합니다. -
왜 이벤트 루프가 단일 스레드에서 실행되는지 궁금 할 것입니까? 스레드는 각 연결에 필요한 데이터에 대해 메모리가 비교적 무겁습니다. 스레드는 시작 해야하는 운영 체제 리소스이며 수천 개의 활성 연결로 확장 할 수 없습니다.
일반적으로 멀티 스레딩은 상황을 복잡하게 할 수 있습니다. 콜백이 데이터를 반환하면 실행중인 스레드에 대한 컨텍스트를 다시 마샬링해야합니다. 스레드 간의 컨텍스트 전환은 통화 스택 또는 로컬 변수와 같은 현재 상태를 동기화해야하므로 느리게 진행됩니다. 이벤트 루프는 여러 스레드가 단일 스레드이기 때문에 리소스를 공유 할 때 버그를 피할 수 있습니다. 단일 스레드 루프는 스레드 안전 에지 케이스를 줄이고 더 빠른 컨텍스트 전환을 가능하게합니다. 이것은 루프 뒤에 진정한 천재입니다. 확장 성을 유지하면서 연결과 실을 효과적으로 활용합니다.
이론은 이제 코드의 모습을 보자. 당신은 당신이 원하는대로 대체로 수행하거나 소스 코드를 다운로드 할 수 있습니다.
반 인피 나이트 루프
이벤트 루프가 대답 해야하는 가장 큰 질문은 루프가 활성화되어 있는지 여부입니다. 그렇다면 콜백 큐에서 얼마나 오래 기다릴 지 결정하십시오. 각 반복에서 루프는 통화 스택 및 폴링을 확장합니다.
이것은 메인 루프를 차단하는 예입니다.
이 코드를 실행하면 루프가 2 초 동안 차단됩니다. 그러나 콜백이 5 초 후에 실행될 때까지 루프는 활성 상태로 유지됩니다. 메인 루프가 차단 해제되면 폴링 메커니즘은 콜백에서 얼마나 기다릴지 결정합니다. 이 루프는 통화 스택이 확장되고 콜백이 남지 않으면 종료됩니다.
콜백 큐
이제 메인 루프를 차단 한 다음 콜백을 예약하면 어떻게됩니까? 루프가 차단되면 대기열에 더 많은 콜백을 추가하지 않습니다.
이 사이클은 7 초 동안 활성화되어 있습니다. 이벤트 루프는 단순성 측면에서 바보입니다. 미래에 무엇을 대기하고 있는지 알 수있는 방법이 없습니다. 실제 시스템에서는 기본 루프를 투표 할 수있을 때 들어오는 콜백이 대기 및 실행됩니다. 이벤트 루프는 차단 해제 될 때 순서대로 여러 단계를 거칩니다. 따라서 루프에 대한 인터뷰에서 눈에 띄기 위해 "이벤트 런처"또는 "원자로 모드"와 같은 멋진 용어를 피하십시오. 단순한 단일 스레드 루프, 동시 및 비 블로킹입니다.
Async/Await을 사용한 이벤트 루프
메인 루프 차단을 피하기 위해 한 가지 아이디어는 동기 I/O를 Async/Await으로 래핑하는 것입니다.
기다린 후 나타나는 것은 콜백 큐에서 나옵니다. 코드는 동기식 차단 코드처럼 보이지만 차단되지 않습니다. Async/await은 readfilesync a setTimeout(
() => console.log('Hi from the callback queue'),
5000); // 保持循环活动这么长时间
const stopTime = Date.now() + 2000;
while (Date.now() < stopTime) {}
로그인 후 복사
로그인 후 복사
로그인 후 복사
thenable <🎜 를 만들어 주 루프에서 제거합니다. 기다린 후에 나타나는 것은 콜백을 통해 비 차단 작업으로 간주 될 수 있습니다.
전체 공개 : 위의 코드는 데모 목적으로 만 사용됩니다. 실제 코드에서는 Fs.ReadFile을 사용하는 것이 좋습니다. 이는 약속 주위에 포장 될 수있는 콜백을 트리거합니다. 전체 의도는 기본 루프에서 I/O 제거를 차단하므로 유효합니다.
한 걸음 더 나아가
이벤트 루프가 콜 스택 및 콜백 큐가 아니라고 말하면 어떻게해야합니까? 이벤트 루프가 하나의 루프가 아니라 다중 루프 인 경우 어떻게해야합니까? 바닥에 여러 스레드가있을 수 있다면 어떨까요?
이제, 나는 당신을 Node.js로 더 깊이 데려 가고 싶습니다.
이벤트 루프 스테이지 <🎜
이것은 이벤트 루프 단계입니다 : <🎜 🎜>
<<>
<<> 그림 출처 : libuv 문서
타임 스탬프 업데이트. 이벤트 루프는 루프 시작시 현재 시간을 캐시하여 빈번한 시간 관련 시스템 호출을 피합니다. 이러한 시스템 호출은 Libuv에 대한 내부 호출입니다.
루프가 활성화되어 있습니까? 루프에 활성 핸들, 활성 요청 또는 닫힌 핸들이 있으면 활성화됩니다. 볼 수 있듯이, 대기열의 보류중인 콜백은 루프를 활성화시킵니다.
만료 타이머를 실행하십시오. Settimeout 또는 SetInterval 콜백이 실행되는 곳입니다. Cached now
를 확인하려면 루프가 만료 된 활성 콜백을 실행할 수 있도록합니다.
대기열에서 보류중인 콜백을 실행합니다. 이전 반복에 의해 콜백이 지연되면이 콜백은 현재 실행됩니다. 폴링은 일반적으로 예외를 제외하고 즉시 I/O 콜백을 실행합니다. 이 단계는 마지막 반복에서 지연된 콜백을 처리합니다. -
유휴 처리기를 실행합니다.이 처리기는 모든 반복에서 실행되며 Libuv의 내부 처리기이기 때문에 부적절한 이름으로 인해 명명합니다.
루프 반복에서 핸들을 setimmediate 콜백으로 실행할 준비를합니다. 이 핸들은 루프가 I/O를 차단하기 전에 실행 되며이 콜백 유형에 대한 대기열을 준비합니다. -
폴링 시간 초과 계산. 루프는 언제 I/O를 차단하는지 알아야합니다. 이것이 타임 아웃을 계산하는 방법입니다
루프가 종료하려고하면 타임 아웃이 0입니다.
활성 핸들이나 요청이 없으면 타임 아웃은 0입니다. -
무료 핸들이 있으면 타임 아웃은 0입니다.
대기열에 보류중인 핸들이있는 경우 타임 아웃은 0입니다.
닫힌 핸들이 있으면 타임 아웃은 0입니다.
위의 어느 것도 없다면, 타임 아웃은 가장 가까운 타이머로 설정되며 활성 타이머가없는 경우 - infinite <🎜 🎜>입니다.
-
사이클링 이전 단계 블록 I/O의 지속 시간. 대기열의 I/O 관련 콜백이 여기에서 실행됩니다. -
체크 핸들 콜백을 실행합니다. 이 단계는 핸들을 준비하기위한 해당 단계 인 Setimmediate Running의 단계입니다. I/O 콜백 실행 중에 대기하는 Setimmediate 콜백이 여기에서 실행됩니다.
닫힌 콜백을 실행하십시오. 이들은 닫힌 연결에서 해제 된 활성 핸들입니다. -
<.> 반복이 끝납니다.
-
블로킹이 없어야 할 때 폴링 블록 I/O가 왜 궁금해할까요? 루프는 큐에 보류중인 콜백이없고 통화 스택이 비어있을 때만 차단됩니다. Node.js에서 가장 가까운 타이머는 예를 들어 Settimeout을 통해 설정할 수 있습니다. Infinite로 설정되면 루프는 들어오는 연결이 더 많은 작업을 수행하기를 기다립니다. 남은 작업이없고 활성 연결이있을 때 폴링이 루프를 활성화시키기 때문에 반 인피 나이트 루프입니다.
다음은 전체 C 코드 형식 의이 타임 아웃 계산의 UNIX 버전입니다.setTimeout(
() => console.log('Hi from the callback queue'),
5000); // 保持循环活动这么长时间
const stopTime = Date.now() + 2000;
while (Date.now() < stopTime) {}
로그인 후 복사
로그인 후 복사
로그인 후 복사
당신은 c에 그다지 익숙하지 않을 수 있지만 이것은 영어처럼 읽히고 7 단계에서 설명한대로 정확하게 수행합니다.
<-st> 단계별 데모 <🎜 🎜>
순수한 자바 스크립트로 각 단계를 표시합니다
파일 I/O 콜백은 4 단계와 9 단계 전에 실행되므로 setimmediate ()가 먼저 발사 될 것으로 예상됩니다.
DNS 쿼리가없는 네트워크 I/O는 메인 이벤트 루프에서 실행되기 때문에 파일 I/O보다 저렴합니다. 파일 I/O는 스레드 풀을 통해 대기됩니다. DNS 쿼리는 스레드 풀도 사용하므로 네트워크 I/O가 파일 I/O만큼 비싸게 만듭니다.
<🎜 🎜> 스레드 풀 <🎜 🎜>
이것은 전체 구조입니다 : <🎜 🎜>
<<> const stopTime = Date.now() + 2000;
while (Date.now() < stopTime) {}
// 这需要 7 秒才能执行
setTimeout(() => console.log('Ran callback A'), 5000);
로그인 후 복사
<<> 그림 출처 : libuv 문서
const fs = require('fs');
const readFileSync = async (path) => await fs.readFileSync(path);
readFileSync('readme.md').then((data) => console.log(data));
console.log('The event loop continues without blocking...');
로그인 후 복사
네트워크 I/O의 경우, 이벤트 루프는 기본 스레드 내에서 폴링합니다. 이 스레드는 다른 스레드와 컨텍스트 스위치가 없기 때문에 스레드 안전이 아닙니다. 파일 I/O 및 DNS 쿼리는 플랫폼에 따라 다르므로 방법은 스레드 풀에서 실행하는 것입니다. 한 가지 아이디어는 위의 코드와 같이 스레드 풀에 들어가는 것을 피하기 위해 DNS 쿼리를 직접 수행하는 것입니다. 예를 들어 LocalHost 대신 IP 주소를 입력하면 풀에서 조회가 제거됩니다. 스레드 풀에서 사용 가능한 스레드 수는 제한되어 있으며 UV_THREADPOOL_SIZE 환경 변수를 통해 설정할 수 있습니다. 기본 스레드 풀 크기는 약 4입니다.
V8은 별도의 루프로 실행되고 통화 스택을 지우고 컨트롤을 이벤트 루프로 반환합니다. V8은 자체 루프 외부의 쓰레기 수집에 여러 스레드를 사용할 수 있습니다. V8을 원래 JavaScript를 가져 와서 하드웨어에서 실행하는 엔진으로 생각하십시오.
일반 프로그래머의 경우, 스레드 안전 문제가 없기 때문에 JavaScript는 단일 스레드로 유지됩니다. V8과 Libuv는 내부적으로 자신의 요구를 충족시키기 위해 자신의 별도의 스레드를 시작합니다.
요약 <🎜 🎜>
콜백이 대기되어 있기 때문에 이벤트 루프는 각 단계에서 계속 반복합니다. 그러나 각 단계에는 다른 유형의 콜백을 대기하는 방법이 있습니다.
process.nexttick () 및 setimmediate () <🎜 🎜
각 단계의 끝에서 process.nexttick () 콜백은 루프에서 실행됩니다. 이 콜백 유형은 각 단계의 끝에서 실행되므로 이벤트 루프의 일부가 아닙니다. setimmediate () 콜백은 전체 이벤트 루프의 일부이므로 이름에서 알 수 있듯이 즉시 실행되지 않습니다. process.nexttick ()은 이벤트 루프의 내부 메커니즘을 이해해야하므로 일반적으로 setimmediate ()를 사용하는 것이 좋습니다.
프로세스가 필요한 몇 가지 이유 .nexttick () : <🎜 🎜>
> 루프가 계속되기 전에 네트워크 I/O가 오류, 정리 또는 재 시도 요청을 처리하도록 허용합니다.
콜 스택이 확장 된 후에는 루프가 계속되기 전에 콜백을 실행해야 할 수도 있습니다.
-
예를 들어 이벤트 송신기는 자체 생성기에서 이벤트를 트리거하려고합니다. 이벤트를 호출하기 전에 통화 스택을 확장해야합니다.
-
통화 스택 확장 허용은 RangeError : 최대 통화 스택 크기와 같은 오류를 방지합니다. 주목해야 할 것은 프로세스를 확인하는 것입니다 .nextTick ()가 이벤트 루프를 차단하지 않도록하는 것입니다. 같은 단계의 재귀 콜백 호출은 차단 문제를 일으킬 수 있습니다.
결론 <🎜 🎜>
이벤트 루프는 궁극적 인 복잡성의 단순성을 구현합니다. 비동기식, 실 안전 및 동시성과 같은 어려운 문제를 해결합니다. 쓸모 없거나 원치 않는 부품을 제거하고 가장 효율적인 방식으로 처리량을 극대화합니다. 따라서 Node.js 프로그래머는 비동기 오류를 쫓는 시간을 줄이고 새로운 기능을 제공하는 데 더 많은 시간을 할애 할 수 있습니다.
node.js 이벤트 루프에 대한 FAQS
node.js 이벤트 루프 란 무엇입니까? Node.js 이벤트 루프는 Node.js가 비 블로킹 비동기 작업을 수행 할 수있는 핵심 메커니즘입니다. 단일 스레드 이벤트 중심 환경에서 I/O 작업, 타이머 및 콜백을 처리 할 책임이 있습니다.
노드 이벤트 루프는 어떻게 작동합니까? 이벤트 루프는 이벤트 큐에서 이벤트 또는 콜백을 지속적으로 확인하고 추가 순서대로 실행합니다. 이벤트의 가용성을 기반으로 이벤트를 처리하는 루프에서 실행되므로 Node.js의 비동기 프로그래밍이 가능합니다. setTimeout(
() => console.log('Hi from the callback queue'),
5000); // 保持循环活动这么长时间
const stopTime = Date.now() + 2000;
while (Date.now() < stopTime) {}
로그인 후 복사
로그인 후 복사
로그인 후 복사
node.js 응용 프로그램에서 이벤트 루프의 역할은 무엇입니까? 이벤트 루프는 node.js의 핵심이므로 응용 프로그램이 반응이 유지되고 여러 스레드없이 많은 동시 연결을 처리 할 수 있습니다.
node.js 이벤트 루프의 단계는 무엇입니까? Node.js의 이벤트 루프에는 타이머, 보류중인 콜백, 유휴, 폴링, 점검 및 폐쇄 등 여러 단계가 있습니다. 이 단계에서는 이벤트가 처리되는 방법과 주문을 결정합니다.
이벤트 루프로 처리되는 가장 일반적인 이벤트 유형은 무엇입니까? 일반적인 이벤트에는 I/O 작업 (예 : 파일 읽기 또는 네트워크 요청 발행), 타이머 (예 : Settimeout 및 SetInterval) 및 콜백 기능 (예 : 비동기 작업의 콜백)이 포함됩니다.
노드 이벤트 루프에서 장기 실행 작업을 처리하는 방법은 무엇입니까? 장기 CPU 집약적 인 작업은 이벤트 루프를 차단할 수 있으며 child_process 또는 worker_threads 모듈과 같은 모듈을 사용하여 하위 프로세스 또는 작업자 스레드에 오프로드해야합니다.
콜 스택과 이벤트 루프의 차이점은 무엇입니까? 통화 스택은 현재 실행 컨텍스트에서 기능 호출을 추적하는 데이터 구조이며, 이벤트 루프는 비동기 및 비 블로킹 작업을 관리하는 데 책임이 있습니다. 이벤트 루프는 콜백 및 I/O 작업의 실행을 예약 한 다음 콜 스택으로 밀기 때문에 함께 작동합니다. 이벤트 루프에서"진드기 "는 무엇입니까? "진드기"는 이벤트 루프의 단일 반복을 나타냅니다. 각 진드기에서 이벤트 루프는 보류중인 이벤트를 확인하고 실행할 준비가 된 콜백을 실행합니다. 진드기는 node.js 응용 프로그램의 기본 작업 단위입니다.
위 내용은 Node.js Event Loop : 개발자의 개념 및 코드 안내서의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!