VUE3.0을 빠르게 시작하는 방법:
JavaScript는 단일 스레드이지만 이벤트 루프는 Node.js가 비차단 I/O 작업을 수행할 수 있도록 시스템 커널을 최대한 사용합니다. 대부분의 최신 커널은 다중 스레드이지만 멀티 스레드 작업을 처리할 수 있습니다. 배경. 작업이 완료되면 커널은 Node.js에 알리고 실행을 위해 적절한 콜백이 루프에 추가됩니다. 이 기사에서는 이 주제를 더 자세히 소개합니다.
Node.js가 실행을 시작하면 이벤트 루프가 발생합니다. 먼저 초기화되고 제공된 입력 스크립트를 처리합니다(또는 이 문서에서 다루지 않는 REPL에 넣습니다). 그러면 비동기 API 호출이 수행되거나 타이머가 예약되거나 process.nextTick()이 호출됩니다. 이벤트 루프 처리를 시작합니다.
다음 그림은 단순화된 이벤트 루프 실행 순서를 보여줍니다.
┌───────────────────────┐ ┌─>│ 타이머 │ │ └─────────────┬─────────────┘ │ ┌─────────────┴────────────┐ │ │ 보류 중인 콜백 │ │ └─────────────┬─────────────┘ │ ┌─────────────┴────────────┐ │ │ 유휴, 준비 │ │ └─────────────┬────────────┘ ┌───────────────┐ │ ┌─────────────┴────────────┐ │ 수신: │ │ │ 설문조사 │<─────┤ 연결, │ │ └─────────────┬─────────────┘ │ 데이터 등 │ │ ┌─────────────┴────────────┐ └───────────────┘ │ │ 확인 │ │ └─────────────┬─────────────┘ │ ┌─────────────┴────────────┐ └──┤ 콜백 닫기 │ └──────────────────────────┘
각 상자는 이벤트 루프의 단계를 나타냅니다.
그러나 각 단계에는 FIFO 대기열 콜백 실행이 있습니다. 일반적으로 이벤트 루프가 단계에 들어가면 현재 단계에서 모든 작업을 수행하고 대기열이 완전히 소비되거나 실행될 때까지 현재 단계의 대기열에서 콜백 실행을 시작합니다. 최대 데이터. 대기열이 소진되거나 최대 크기에 도달하면 이벤트 루프가 다음 단계로 이동합니다.
이벤트 루프의 각 프로세스에서, Node.js는 비동기 I/O와 타이머를 기다리고 있는지 확인하고 그렇지 않은 경우
타이머는 원하는 시간이 아닌 콜백이 실행될 중요한 지점을 지정합니다
.타이머는 지정된 경과 시간 이후 가능한 한 빨리 실행되지만 운영 체제 스케줄링이나 기타 콜백으로 인해 실행이 지연될 수 있습니다.
기술적으로 말하면 폴링 단계는 콜백이 실행되는 시점을 결정합니다.
예를 들어 타이머를 100ms 후에 실행하도록 설정했지만 스크립트는 파일을 비동기식으로 읽고 95ms가 걸립니다.
const fs = require('fs') ; 함수 someAsyncOperation(콜백) { // 완료하는 데 95ms가 걸린다고 가정합니다. fs.readFile('/path/to/file', 콜백); } const timeoutScheduled = Date.now(); setTimeout(() => { const 지연 = Date.now() - timeoutScheduled; console.log(`예약된 이후 ${delay}ms가 지났습니다`); }, 100); // 완료하는 데 95ms가 걸리는 someAsyncOperation을 수행합니다. someAsyncOperation(() => { const startCallback = Date.now(); // 10ms가 걸리는 작업을 수행합니다... while (Date.now() - startCallback < 10) { //아무것도 하지 않음 } });
이벤트 루프가 폴링 단계에 들어가면 빈 대기열(fs.readFile()이 아직 완료되지 않음)이 있으므로 가장 빠른 타이머 임계값인 fs에 도달할 때까지 남은 밀리초 동안 기다립니다. .readFile()은 파일 읽기를 완료했으며 폴링 단계에 추가하고 실행을 완료하는 데 10ms가 걸립니다. 콜백이 완료되면 대기열에 실행할 콜백이 없으며 이벤트 루프가 타이머 단계로 돌아갑니다. 타이머 콜백을 실행합니다. 이 예에서는 실행 전 타이머가 105ms 동안 지연되는 것을 볼 수 있습니다.
이벤트 루프를 차단하는 폴 단계를 방지하기 위해 libuv(플랫폼의 이벤트 루프 및 모든 비동기 동작을 구현하는 C 언어 라이브러리)도 있습니다. 폴 단계 최대 폴링 중지
이 단계는 특정 시스템 작업(예: TCP 오류 유형)에 대한 콜백을 실행합니다. 예를 들어, 일부 *nix 시스템은 연결을 시도할 때 TCP 소켓이 ECONNREFUSED를 수신하는 경우 오류가 보고될 때까지 기다리기를 원합니다. 이는 보류 중인 콜백 단계 동안 실행을 위해 대기열에 추가됩니다.
폴 단계에는 두 가지 주요 기능이 있습니다
다음 두 가지 일이 발생합니다.
비어 있으면
즉시 실행합니다.타이머가 만료되었는지 여부를 감지합니다. 그렇다면 이벤트 루프는 타이머 단계에 도달하고 이 단계에서 타이머 콜백을 실행합니다
폴링 단계가 완료된 후 즉시 콜백을 실행할 수 있습니다. 폴링 단계가 유휴 상태가 되고 스크립트가 setImmediate()를 사용하여 대기열에 추가된 경우 이벤트 루프는 기다리는 대신 확인 단계를 계속할 수 있습니다.
setImmediate()는 실제로 이벤트 루프의 별도 단계에서 실행되는 특수 타이머입니다. libuv API를 사용하여 폴링 단계가 완료된 후 실행될 콜백을 예약합니다.
일반적으로 코드가 실행됨에 따라 이벤트 루프는 결국 폴링 단계에 도달하여 들어오는 연결, 요청 등을 기다립니다. 그러나 setImmediate()를 사용하여 콜백이 예약되고 폴 단계가 유휴 상태가 되면 폴 이벤트를 기다리는 대신 종료되고 확인 단계가 계속됩니다.
setImmediate()를
통해 전송됩니다
.와 setTimeout()은 비슷하지만 호출 시기에 따라 다르게 동작합니다.
각 콜백이 실행되는 순서는 다음에 따라 다릅니다. 호출 순서 이 환경에서 동일한 모듈이 동시에 호출되면 프로세스 성능에 따라 시간이 제한됩니다(
예를 들어
이 시스템에서 실행되는 다른 응용 프로그램의 영향도 받습니다)., I/O에서 다음 스크립트를 실행하지 않으면 프로세스 성능에 영향을 받지만 이 두 타이머의 실행 순서를 결정할 수 없습니다.
// timeout_vs_immediate.js setTimeout(() => { console.log('타임아웃'); }, 0); setImmediate(() => { console.log('즉시'); });
$ 노드 timeout_vs_immediate.js 시간 초과 즉각적인 $ 노드 timeout_vs_immediate.js 즉각적인 timeout
그러나 I/O 루프로 이동하면 즉시 콜백이 항상 먼저 실행됩니다.
// timeout_vs_immediate.js const fs = require('fs'); fs.readFile(__filename, () => { setTimeout(() => { console.log('타임아웃'); }, 0); setImmediate(() => { console.log('즉시'); }); });
$ 노드 timeout_vs_immediate.js 즉각적인 시간 초과 $ 노드 timeout_vs_immediate.js 즉각적인setTimeout에 비해 setImmediate
시간 초과의 장점은
존재하는 타이머 수에 관계없이 setImmediate가 항상 I/O의 타이머보다 먼저 실행된다는 것입니다.
process.nextTick()은 비동기 API의 일부이지만, process.nextTick()은 이벤트 루프 기술의 일부가 아니기 때문에 다이어그램에 표시되지 않는다는 것을 알 수 있습니다. 현재 작업이 실행됩니다. 완료 후 nextTickQueue는 이벤트 루프의 현재 단계에 관계없이 실행됩니다. 여기서 작업은 기본 C/C++ 핸들러의 변환으로 정의되며 실행해야 하는 JavaScript를 처리합니다. 다이어그램에 따르면 어떤 단계에서든 process.nextTick()을 호출할 수 있습니다. process.nextTick()에 전달된 모든 콜백은 이벤트 루프가 계속 실행되기 전에 실행됩니다. 이로 인해 재귀적으로 호출할 수 있으므로 일부 나쁜 상황이 발생할 수 있습니다. .process.nextTick() I/O를 "고갈"시켜 이벤트 루프가 폴링 단계에 진입하는 것을 방지합니다.
Node.js에 이러한 상황이 포함된 이유는 무엇입니까? Node.js의 디자인 철학은 API가 꼭 그럴 필요는 없더라도 항상 비동기식이어야 한다는 것이므로 다음 스니펫
함수 apiCall(arg, callback) {을살펴보세요.
if (인수 유형 !== '문자열') 반환 프로세스.nextTick( 콜백, new TypeError('인수는 문자열이어야 합니다.') ); }
스니펫은 매개변수 확인을 수행하고 잘못된 경우 오류를 콜백에 전달합니다. API는 최근 process.nextTick()에 매개변수를 전달할 수 있도록 업데이트되어 콜백 이후 전달된 모든 매개변수를 콜백에 대한 인수로 허용하므로 함수를 중첩할 필요가 없습니다.
우리가 하고 있는 일은 오류를 사용자에게 다시 전달하는 것입니다. 그러나 이는 사용자의 나머지 코드가 실행되도록 허용하는 경우에만 해당됩니다. process.nextTick()을 사용하면 apiCall()이 항상 사용자 코드의 나머지 부분 이후와 이벤트 루프가 계속되도록 허용하기 전에 콜백을 실행하도록 보장합니다. 이를 달성하기 위해 JS 호출 스택이 해제된 다음 제공된 콜백이 즉시 실행됩니다. 이를 통해 RangeError: Maximum call stack size listed as of v8이 발생하지 않고 process.nextTick()에 대한 재귀 호출을 수행할 수 있습니다.