노드의 "이벤트 루프"는 큰 동시성과 높은 처리량을 처리하는 능력의 핵심입니다. 이것은 가장 마법의 장소입니다. 이에 따르면, Node.js는 기본적으로 "단일 스레딩"으로 이해 될 수 있으며, 백그라운드에서 임의의 작업을 처리 할 수 있습니다. 이 기사는 이벤트 루프의 작동 방식을 보여 주며 마법도 느낄 수 있습니다.
이벤트 중심 프로그래밍
이벤트 루프를 이해하려면 먼저 이벤트 드라이브 프로그래밍을 이해해야합니다. 1960 년에 나타났습니다. 오늘날 이벤트 중심 프로그래밍은 UI 프로그래밍에 널리 사용됩니다. JavaScript의 주요 용도 중 하나는 DOM과 상호 작용하는 것이므로 이벤트 기반 API를 사용하는 것이 당연합니다.
간단히 정의하십시오 : 이벤트 중심 프로그래밍은 이벤트 또는 상태의 변경을 통해 응용 프로그램 프로세스를 제어합니다. 일반적으로 이벤트 모니터링을 통해 구현됩니다. 이벤트가 감지되면 (즉, 상태 변경) 해당 콜백 함수가 호출됩니다. 친숙하게 들리나요? 실제로 이것은 node.js 이벤트 루프의 기본 작업 원리입니다.
클라이언트 측 JavaScript 개발에 익숙한 경우 DOM 요소와 결합하여 사용자 상호 작용을 통과하는 데 사용되는 Element.onClick ()과 같은 .on*() 메소드에 대해 생각해보십시오. 이 작업 모드를 사용하면 단일 인스턴스에서 여러 이벤트를 트리거 할 수 있습니다. Node.js는 소켓과 같은 Eventemitter (이벤트 생성기)를 통해이 모드를 트리거합니다. 단일 인스턴스에서 하나 이상의 상태 변경이 트리거 될 수 있습니다.
또 다른 일반적인 패턴은 성공과 실패를 표현하는 것입니다. 일반적으로 두 가지 공통 구현 방법이 있습니다. 첫 번째는 "오류 예외"를 콜백으로 전달하는 것입니다. 이는 일반적으로 첫 번째 매개 변수로 콜백 함수로 전달됩니다. 두 번째 유형은 약속 디자인 모드를 사용하는 것이며 ES6이 추가되었습니다. 참고* Promise Mode는 jQuery와 같은 함수 체인 쓰기 방법을 사용하여 다음과 같은 깊은 콜백 기능 중첩을 피합니다.
코드 사본은 다음과 같습니다.
$ .getJson ( '/getUser'). done (successHandler) .fail (실패)
"FS"(파일 시스템) 모듈은 주로 콜백에 예외를 전달하는 스타일을 사용합니다. fs.readfile () 첨부 된 이벤트와 같은 특정 통화를 기술적으로 트리거하지만 API는 사용자가 작동의 성공 또는 실패를 표현하도록 상기시키는 데만 사용됩니다. 이러한 API는 기술적 인 제한이 아니라 건축상의 이유로 선택됩니다.
일반적인 오해는 이벤트를 트리거 할 때 이벤트 이미 터들도 본질적으로 비동기식이라는 것입니다. 다음은 이것을 증명하기위한 간단한 코드 스 니펫입니다.
코드 사본은 다음과 같습니다.
함수 myemitter () {
이벤트 미터 .call (this);
}
util.inherits (myemitter, eventemitter);
myemitter.prototype.dostuff = function dostuff () {
Console.log ( '이전')
imitter.emit ( 'Fire')
Console.log ( 'After')}
};
var me = new myemitter ();
me.on ( 'fire', function () {
Console.log ( 'Emit Fired');
});
me.dostuff ();
// 출력 :
// 전에
// 방출 된 발사
// 후에
참고* Emitit.emit이 비동기 인 경우 출력이 있어야합니다
// 전에
// 후에
// 방출 된 발사
이벤트 미터는 종종 비동기식으로 완료 해야하는 작업에 알리는 데 사용되기 때문에 종종 비동기식으로 나타납니다. 청취 함수는 비동기 적으로 실행될 수 있지만 모든 청취 기능은 추가 된 순서대로 동기식으로 실행됩니다.
메커니즘 개요 및 스레드 풀링
노드 자체는 여러 라이브러리에 의존합니다. 그중 하나는 비동기 이벤트 대기열과 실행을 마술처럼 처리하는 라이브러리 인 Libuv입니다.
노드는 가능한 많은 기존 기능을 사용하여 운영 체제 커널을 사용합니다. 예를 들어, 응답 요청이 생성되고 연결이 전달되고 처리를 위해 시스템에 위임됩니다. 예를 들어, 들어오는 연결은 노드로 처리 될 때까지 운영 체제를 통해 대기합니다.
노드가 스레드 풀이 있다고 들었을 수도 있고 "노드가 작업을 순서대로 처리하면 스레드 풀이 필요한 이유는 무엇입니까?" 커널에서 모든 작업이 비동기 적으로 실행되는 것은 아니기 때문입니다. 이 경우 Node.js는 작동하는 동안 스레드를 일정 시간 동안 잠글 수있어서 차단되지 않고 이벤트 루프를 계속 실행할 수 있어야합니다.
다음은 내부 작동 메커니즘을 보여주는 간단한 예제 다이어그램입니다.
┌ ─건평가 유바르기
─* ► ► 타임저 │
│ │ │ 익은신 안정장입니다 대한 ─건밭은 ─캠입니다 대한 신 안타입니다. UN
│ │ │ 착수 유장 이신다 유자 유추 께 ─건밭 은신 안정장입니다. UN. 출신입니다 대한 신 안에림이 있습니다. UN
│ │ 콜백 보류 │ │
│ │ │ ─퀴얼은 유장 유바레입니다. 신 안에림 은신 안정장입니다. UNFER 신안은 ─ 안 채우는 신 안에 담장 ┌ ┌ ┌ ┌ 담사입니다. · 뇨 ─ 안타 유자입니다
│ │ │ 익은신 안정장 a │ │ │ 담사 : │ │ │ 담사 : │ │ 담사
connections │ │ │ │ │◄ · 연결, │
│ │ │ 익은신 안정장 데이터 등입니다. 테
│ │ │ ─퀴얼 은신 안정장입니다 U 초, 유바레입니다 대한 신 안타임 신짐 은신 안타임입니다 대한 신 안에림이 있습니다. UN. 신 안용 유자입니다 대한 신 안에 유고입니다. UN
효소보자 setimmediate │
└ ─건평가 유바르기
이벤트 루프의 내부 작업 메커니즘을 이해하는 데 어려움이 있습니다.
모든 콜백은 이벤트 루프의 한 단계 (예 : 타이머)가 끝나기 전에 process.nexttick ()을 통해 사전 설정되고 다음 단계로 전환됩니다. 이렇게하면 Process.nextTick ()에 대한 잠재적 인 재귀 호출을 피하면 무한 루프가 발생합니다.
"Pending Callbacks"는 다른 이벤트 루프 사이클 (예 : fs.write로 전달됨)으로 처리되지 않는 콜백 큐의 콜백입니다.
이벤트 이미 터 및 이벤트 루프
이벤트 미터를 만들면 이벤트 루프와의 상호 작용을 단순화 할 수 있습니다. 이벤트 기반 API를보다 쉽게 만들 수있는 보편적 인 캡슐화입니다. 두 상호 작용이 종종 개발자가 혼란스러워하는 방식.
다음 예는 이벤트가 동기식으로 유발된다는 것을 잊어 버리면 이벤트가 놓칠 수 있음을 보여줍니다.
코드 사본은 다음과 같습니다.
// v0.10 이후에 ( '이벤트') 요구 사항이 더 이상 필요하지 않습니다
var eventimitter = require ( 'events');
var util = 요구 ( 'util');
function mything () {
이벤트 미터 .call (this);
dofirstthing ();
this.emit ( 'thing1');
}
util.inherits (mything, eventemitter);
var mt = new Mything ();
mt.on ( 'the the the onthing1 () 함수 onthing1 () {
// 죄송합니다.이 사건은 결코 일어나지 않을 것입니다
});
위의 'Thing1'이벤트는 mything ()에 의해 잡히지 않을 것입니다. mything ()는 이벤트를 듣기 전에 인스턴스화해야하기 때문입니다. 다음은 추가 클로저를 추가하지 않고도 간단한 해결 방법입니다.
코드 사본은 다음과 같습니다.
var eventimitter = require ( 'events');
var util = 요구 ( 'util');
function mything () {
이벤트 미터 .call (this);
dofirstthing ();
setimmediate (emitthing1, this);
}
util.inherits (mything, eventemitter);
함수 방출 1 (self) {
self.emit ( 'Thing1');
}
var mt = new Mything ();
mt.on ( 'the the the onthing1 () 함수 onthing1 () {
// 실행하다
});
다음 체계도 작동 할 수 있지만 일부 성능이 손실됩니다.
코드 사본은 다음과 같습니다.
function mything () {
이벤트 미터 .call (this);
dofirstthing ();
// function#bind ()를 사용하면 성능이 떨어집니다
setimmediate (this.emit.bind (this, 'the the the '1'));
}
util.inherits (mything, eventemitter);
또 다른 문제는 오류 (예외)를 트리거하는 것입니다. 응용 프로그램에서 문제를 찾기가 이미 어렵지만 통화 스택 (참고 *E.stack)이 없으면 디버깅이 거의 불가능합니다. 원격 비동기식으로 오류가 요청되면 통화 스택이 손실됩니다. 가능한 두 가지 솔루션이 있습니다. 동기 트리거링 또는 다른 중요한 정보와 함께 오류가 전달되도록합니다. 다음 예는이 두 가지 솔루션을 보여줍니다.
코드 사본은 다음과 같습니다.
mything.prototype.foo = function foo () {
//이 오류는 비동기식으로 트리거됩니다
var er = dofirstthing ();
if (er) {
// 트리거 할 때는 현장 통화 스택 정보를 유지하는 새로운 오류를 만들어야합니다.
setImmediate (EmiterRor, this, new 오류 ( '나쁜 것'));
반품;
}
// 오류를 트리거하고 즉시 처리 (동기화)
var er = dosecondthing ();
if (er) {
this.emit ( '오류', '더 나쁜 것들');
반품;
}
}
상황을 평가하십시오. 오류가 트리거되면 즉시 처리 할 수 있습니다. 대안 적으로, 그것은 사소 할 수 있으며 쉽게 처리하거나 나중에 처리 할 수 있습니다. 또한, 구성된 객체 인스턴스가 불완전 할 가능성이 있기 때문에 생성자를 통해 오류를 전달하는 것은 좋은 생각이 아닙니다. 예외는 오류가 지금 직접 던져진 경우입니다.
결론
이 기사는 이벤트 루프의 내부 작동 메커니즘과 기술적 세부 사항을 간략하게 탐구합니다. 모두 신중하게 고려됩니다. 또 다른 기사는 이벤트 루프와 시스템 커널 간의 상호 작용에 대해 논의하고 비동기로 실행되는 Nodejs의 마법을 보여줍니다.