Back to the Basics

[Node.js]- Node.js v17.0.1 documentation - Event 본문

Language/JavaScript & Node.js

[Node.js]- Node.js v17.0.1 documentation - Event

9Jaeng 2021. 10. 31. 14:14
728x90
반응형

Events

이 글은 Node.js Ver 17.0.1 documentation을 읽고 정리한 글입니다. 개인적인 정리이니 의역 다분함을 명심해 주세요.

Node.js의 대부분 API는 event를 내보내는 "event 기반의 아키텍처"를 사용해서 만들어졌다. emitter라는 특정한 종류의 객체가 내보내는 event는 listeners라고 불리는 함수를 호출한다.

예를 들어, net.Server Object는 peer가 연결할 때마다 event를 내보내고, fs.ReadStream은 file이 열릴 때마다 event를 내보낸다. 그리고 stream은 data가 읽을 수 있을 때마다 event를 내보낸다. 이와 같이 events를 내보내는 모든 객체는 EventEmitter Class의 Instance이다.

이러한 객체는 하나 이상의 함수를 객체에서 내보내는 event에 연결할 수 있는 eventEmitter.on()함수를 표시한다. 일반적으로 이벤트 이름은 camel-cased strings이지만, JS 속성 키를 사용할 수 있다.

EventEmitter 객체가 event를 내보낸 때, 특정 event에 연결되어있는 함수들은 모두 동기적으로 호출된다. 호출된 listener에 의해 반환된 값은 무시되거나 버려진다.

아래는 단일 listener가 있는 간단한 EventEmitter 예시 코드이다.

  • eventEmitter.on(eventName,listener)
    • listener를 등록하기 위해 사용되는 method
    • eventName : event의 name
    • listner : callback function
  • eventEmitter.emit() : event를 trigger 하는 데 사용되는 method
const EventEmitter=require('events')

const myEmitter=new EventEmitter();

// ex 1 

myEmitter.on('event',()=>{
console.log('an evnet occurred!!')
});

myEmitter.emit('event')

// ans : an evnet occurred!!

// ex 2 
// 이 예제는 두 개의 listner를 등록한 경우

function cb1() {
  console.log("an event cooured!!!!");
}

function cb2() {
  console.log("Another event occured");
}

// 하나의 이름으로 두 개의 callback을 등록한다면, eventOne이 발생될 때 두 개의 callback이 호출된다.

myEmitter.on("eventOne", cb1);
myEmitter.on("eventOne", cb2);

myEmitter.emit("eventOne"); // event call

/* ans
an event cooured!!!!
Another event occured
*/

Passing arguments and this to listeners

eventEmitter.emit함수는 임의의 복수 인수를 listner 함수에 전달할 수 있다. 그리고, 이 callback 함수 내에서 사용된 this는 listener가 연결된 EventEmitter 개체를 참조하도록 설정된다.

아래의 예시에서 처럼 this를 _출력했을 때 해_당 listener가 연결된 EventEmitter 객체인 myEmitter가 출력된다.

// ! Passing arguments and this to listeners
{
  myEmitter.on("event", function (a, b) {
    console.log(a, b, this, this === myEmitter);
  });
  myEmitter.emit("event", "a", "b");
}

/*
a b 
EventEmitter {
  _events: [Object: null prototype] { event: [Function (anonymous)] },
  _eventsCount: 1,
  _maxListeners: undefined,
  [Symbol(kCapture)]: false
} 
true
*/

하지만, Arrow function을 사용하면 "this" 키워드는 EventEmiter를 참조하지 않는다.

{
  myEmitter.on("event2", (a, b) => {
    console.log(a, b, this);
  });
  myEmitter.emit("event2", "a", "b");
}

/* and
a b {}
*/

Asynchronous VS Synchronous

EventEmitter는 등록된 모든 listener를 등록된 수서대로 동기적으로 실행한다. 이는 event의 적절한 순서를 보장하고 논리적 오류 또는 race condition을 방지할 수 있다.

비동기적으로 사용하기 위해서는 setImmediate() 또는 process.nextTick() 메서드를 사용할 수 있다.

// ! Asynchronouse vs Synchronous

const myEmitter3 = new EventEmitter();
myEmitter3.on("event", (a, b) => {
  setImmediate(() => {
    console.log("this happens asynchronously");
  });
});
myEmitter3.emit("event", "a", "b");
console.log("first?");
console.log("second");
/*
irst?
second
this happens asynchronously
*/

Handling events only once

eventEmitter.on()을 통해 등록된 listener는 해당 event가 발생될 때마다 불려진다. 만약 이 listener 하 딱 한 번만 호출되게 하려면 eventEmitter.once() 메서드를 사용하면 가능하다. 이 메서드를 통해 event가 불려지면 listener의 등록이 취소되고 호출된다.

// ! Handling events only once

const myEmitter4 = new EventEmitter();
let count = 0;
myEmitter4.on("event", () => console.log(++count));

myEmitter4.emit("event");
// ans : 1
myEmitter4.emit("event");
// and : 2

Error events

EventEmitter에서 error가 발생하면 erro event를 보내는 것이 일반적이다. 이 error event는 Node.js 내에서 특 혈반 case로 취급된다.

만약 EventEmitter에 등록된 error listener가 없다면, error 발생 시 Node.js는 process를 종료하기 때문에 "error" event를 추가해야 한다.

// ! Error events

const myEmitter5 = new EventEmitter();

// myEmitter5.emit("error", new Error("whoops!"));
// Throew and crashe Node.js

myEmitter5.on("error", (err) => console.error("whoops! there was an error"));
myEmitter5.emit("error", new Error("whoops!"));
// ans : whoops! there was an error

events.errorMonitor symbol을 listener에 설치함으로써 error event를 monitoring 할 수 있다.

Capture rejections of promises

async 함수를 evnet handler와 사용할 때 발생된 예외에 대해 처리되지 않은 거부를 초래할 수 있기 때문이다.

문서의 마지막에서는

"The 'error' events that are generated by the captureRejections behavior do not have a catch handler to avoid infinite error loops: the recommendation is to not use **async** functions as **'error'** event handlers."

async function은 'error' event handler로 사용하지 말라고 한다.

728x90
반응형
Comments