#### # Node.js - events Module & EventEmitter Class
- EventEmitter는 Node.JS에 내장 옵저버 패턴의 구현
#### # 참고
- [https://www.haruair.com/blog/3396](https://www.haruair.com/blog/3396)
- [nodejs.org : events](https://nodejs.org/dist/v5.5.0/docs/api/events.html)
#### # event_emitter.js
```javascript
var events = require('events');
var eventEmitter = new events.EventEmitter();
var connectHandler = function connected(){
console.log("Connection Successful");
eventEmitter.emit("data_received");
}
eventEmitter.on('connection', connectHandler);
eventEmitter.on('data_received', function(){
console.log("Data Received");
});
eventEmitter.emit('connection');
console.log("Program has ended");
```
#### # 실행
$ node event_emitter.js
#### # 다음은 EventEmitter 인스턴스를 단일 리스너와 함께 작성한 예다.
1. eventEmitter.on() 메소드는 리스너를 등록하는데 사용한다.
2. 그리고 eventEmitter.emit() 메소드를 통해 등록한 이벤트를 호출할 수 있다.
#### # event_emitter2.js
```javascript
const EventEmitter = require('events');
const util = require('util');
function MyEmitter() {
EventEmitter.call(this);
}
util.inherits(MyEmitter, EventEmitter);
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
myEmitter.emit('event');
&
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
myEmitter.emit('event');
```
#### # 인자와 this를 리스너에 전달하기
- eventEmitter.emit() 메소드는 인자로 받은 값을 리스너 함수로 전달한다.
- 이 과정에서 기억해야 할 부분이 있는데
- 일반적으로 EventEmitter를 통해 호출되는 리스너 함수 내에서는
- this가 이 리스너 함수를 부착한 EventEmitter를 참조하도록 의도적으로 구현되어 있다.
```javascript
const myEmitter = new MyEmitter();
myEmitter.on('event', function(a, b) {
console.log(a, b, this);
// Prints:
// a b MyEmitter {
// domain: null,
// _events: { event: [Function] },
// _eventsCount: 1,
// _maxListeners: undefined }
});
myEmitter.emit('event', 'a', 'b');
```
#### # ES6의 Arrow 함수를 리스너로 사용하는 것은 가능하지만 이 기능의 명세대로
#### 이 함수 내에서의 this는 더이상 EventEmitter 인스턴스를 참조하지 않는다.
```js
const myEmitter = new MyEmitter();
myEmitter.on('event', (a, b) => {
console.log(a, b, this);
// Prints: a b {}
});
myEmitter.emit('event', 'a', 'b');
```
#### # EventListener는 모든 리스너를 등록한 순서대로 동기적으로 처리한다.
- 이벤트를 적절한 순서로 처리하는 것을 보장해 경쟁 조건(race condition)이나 로직 오류를 피하는 것이 중요하다.
- 이 모든 것이 적절하게 구현되었을 때, setImmediate()이나 process.nextTick()메소드를
- 사용해 리스너 함수를 비동기도 동작하도록 전환할 수 있다.
```js
const myEmitter = new MyEmitter();
myEmitter.on('event', (a, b) => {
setImmediate(() => {
console.log('this happens asynchronously');
});
});
myEmitter.emit('event', 'a', 'b');
```
#### # 단 한 번만 동작하는 이벤트
- eventEmitter.on() 메소드로 등록된 리스너는 이벤트 이름이 호출되는 매 횟수만큼 실행된다.
```javascript
const myEmitter = new MyEmitter();
var m = 0;
myEmitter.on('event', () => {
console.log(++m);
});
myEmitter.emit('event');
// Prints: 1
myEmitter.emit('event');
// Prints: 2
```
#### # 오류 이벤트
```javascript
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));
```
#### # Node.js 프로세스가 멈추는 것을 막기 위해서는
```javascript
process.on('uncaughtException') 이벤트에 리스너를 등록하거나
domain 모듈을 사용할 수 있다. (하지만 domain 모듈은 더이상 사용하지 않는다(deprecated))
const myEmitter = new MyEmitter();
process.on('uncaughtException', (err) => {
console.log('whoops! there was an error');
});
myEmitter.emit('error', new Error('whoops!'));
// Prints: whoops! there was an error
// 개발자가 항상 'error' 이벤트에 리스너를 등록하는 것이 가장 좋은 방법이다:
const myEmitter = new MyEmitter();
myEmitter.on('error', (err) => {
console.log('whoops! there was an error');
});
myEmitter.emit('error', new Error('whoops!'));
// Prints: whoops! there was an error
```
#### # 클래스: EventEmitter
- EventEmitter 클래스는 events 모듈에 의해서 정의되고 제공된다.
```javascript
const EventEmitter = require('events');
```
- 모든 EventEmitters는
- 새로운 이벤트를 등록할 때마다 'newListner' 이벤트를 호출하고
리스너를 제거할 때마다 'removeListner'를 호출한다.
#### # 이벤트: ‘newListener’
- event {String|Symbol} 이벤트명
- listener {Function} 이벤트 처리 함수
- EventEmitter 인스턴스는 인스턴스 자신의 'newListener' 이벤트를 리스너를
내부 리스너 배열에 추가하기 전에 호출한다.
- 'newListener' 이벤트에 리스너가 전달되기 위해 이벤트 명칭과 추가될 리스너의 참조가 전달된다.
- 실제로 리스너가 추가되기 전에 이 이벤트가 호출된다는 점으로 인해 다음과 같은 중대한 부작용이 나타날 수 있다.
어떤 추가적인 리스너든 동일한 명칭의 리스너를 'newListener' 콜백에서 먼저 등록한다면 추가하려는
해당 리스너가 실제로 등록되기 전에 이 함수가 먼저 추가될 것이다.
```javascript
const myEmitter = new MyEmitter();
// Only do this once so we don't loop forever
myEmitter.once('newListener', (event, listener) => {
if (event === 'event') {
// Insert a new listener in front
myEmitter.on('event', () => {
console.log('B');
});
}
});
myEmitter.on('event', () => {
console.log('A');
});
myEmitter.emit('event');
// Prints:
// B
// A
```
#### # 이벤트: ‘removeListener’
- event {String|Symbol} 이벤트명
- listener {Function} 이벤트 처리 함수
'removeListener' 이벤트는 리스너가 제거된 후에 실행된다.
#### # EventEmitter.listenerCount(emitter, event)
- 안정성: 0 – 추천 안함: emitter.listenerCount()을 대신 사용할 것.
- 인자로 넘긴 emitter에 해당 event가 리스너를 몇 개나 갖고 있는지 확인하는 클래스 메소드다.
```javascript
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {});
myEmitter.on('event', () => {});
console.log(EventEmitter.listenerCount(myEmitter, 'event'));
// Prints: 2
```
#### # EventEmitter.defaultMaxListeners
- 기본값으로 한 이벤트에 최대 10개 리스너를 등록할 수 있다.
- 이 제한은 각각의 EventEmitter의 인스턴스에서 emitter.setMaxListeners(n) 메소드로 지정할 수 있다.
- 모든 EventEmitter 인스턴스의 기본값을 변경하려면 EventEmitter.defaultMaxListeners 프로퍼티를 사용할 수 있다.
- EventEmitter.defaultMaxListeners 설정을 변경할 때는
모든 EventEmitter 인스턴스에게 영향을 주기 때문에 이 변경 이전에 만든 부분에 대해서도 주의해야 한다.
하지만 emitter.setMaxListeners(n)를 호출해서 설정한 값이 있다면
EventEmitter.defaultMaxListeners의 값보다 우선으로 적용된다.
- 참고로 이 값은 강제적인 제한이 아니다.
EventEmitter 인스턴스는 제한된 값보다 더 많은 리스너를 추가할 수 있지만
EventEmitter 메모리 누수의 가능성이 있는 것으로 보고
stderr를 통해 경고를 보내 개발자가 문제를 인지할 수 있게 한다.
- 어떤 EventEmitter든
- emitter.getMaxListeners()와 emitter.setmaxListeners() 메소드를 사용해
이 경고를 임시로 피할 수 있다:
```javascript
emitter.setMaxListeners(emitter.getMaxListeners() + 1);
emitter.once('event', () => {
// do stuff
emitter.setMaxListeners(Math.max(emitter.getMaxListeners() - 1, 0));
});
```
#### # emitter.addListener(event, listener)
- emitter.on(event, listener)의 별칭이다.
#### # emitter.emit(event[, arg1][, arg2][, …])
- event에 등록된 리스너를 등록된 순서에 따라 동기적으로 호출한다.
제공되는 인자를 각각 리스너로 전달한다.
- 이벤트가 존재한다면 true, 그 외에는 false를 반환한다.
#### # emitter.getMaxListeners()
- 현재 EventEmitter에 지정된 최대 리스너 수를 반환한다.
- 기본값은 EventEmitter.defaultMaxListeners이며 emitter.setMaxListeners(n)로
변경했을 경우에는 그 값을 반환한다.
#### # emitter.listenerCount(event)
- event {Value} The type of event
- event {Value} 이벤트 이름
해당 event 이름에 등록되어 있는 리스너의 수를 반환한다.
#### # emitter.listeners(event)
- 해당 event에 등록된 리스너 배열의 사본을 반환한다.
```javascript
server.on('connection', (stream) => {
console.log('someone connected!');
});
console.log(util.inspect(server.listeners('connection')));
// Prints: [ [Function] ]
```
#### # emitter.on(event, listener)
- listener 함수를 지정한 event의 리스너 배열 가장 끝에 추가한다.
- listener가 이미 추가되어 있는 함수인지 확인하는 과정이 없다.
같은 조합의 event와 listener를 여러 차례 추가했다면 추가한 만큼 여러번 호출된다.
```javascript
server.on('connection', (stream) => {
console.log('someone connected!');
});
EventEmitter의 참조를 반환하기 때문에 연속해서 호출하는 것이(chaining) 가능하다.
```
#### # emitter.once(event, listener)
- 일회성 listener 함수를 event에 등록한다.
- 이 이벤트는 다음 차례 event가 호출될 때 한 번만 실행한 후 제거된다.
```javascript
server.once('connection', (stream) => {
console.log('Ah, we have our first user!');
});
EventEmitter의 참조를 반환하기 때문에 연속해서 호출하는 것이 가능하다.
```
#### # emitter.removeAllListeners([event])
- 모든 리스너, 또는 지정한 event의 리스너를 제거한다.
- 코드 다른 곳에 추가되어 있는 리스너를 제거하는 것은 좋지 않은 방법이다.
- 특히 EventEmitter 인스턴스가 다른 컴포넌트나 모듈에서 생성되었을 때는 더 유의해야 한다.
(예를 들어, 소켓이나 파일 스트림 등)
- EventEmitter의 참조를 반환하기 때문에 연속해서 호출하는 것이 가능하다.
#### # emitter.removeListener(event, listener)
- 특정 event에 등록되어 있는 특정 listener를 제거한다.
```javascript
var callback = (stream) => {
console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
```
- removeListener는 한 인스턴스 내에 존재하는 리스너 배열에서 해당 리스너를 하나 제거한다.
만약 한 리스너를 여러 차례 특정 event에 등록해서 배열 내 여러 개 존재한다면,
그 모든 리스너를 지우기 위해서는 removeListener를 여러 번 호출해야 한다.
- 이 메소드로 리스너가 하나 지워진 후에 등록되어 있는
리스너의 위치 인덱스가 변경되는데 리스너가 내부 배열로 관리되기 때문이다.
- 이 동작은 리스너가 호출되는 순서에는 영향을 주지 않지만
emitter.listeners() 메소드로 생성한
리스너 배열의 사본이 있다면 다시 생성해야 할 필요가 있다.
- EventEmitter의 참조를 반환하기 때문에 연속해서 호출하는 것이 가능하다.
#### # emitter.setMaxListeners(n)
- EventEmitters의 기본값인 10보다 더 많은 리스너가 등록되어 있다면 경고가 출력된다.
이 함수는 어디에서 메모리 누수가 발생하는지 찾는데 유용하다.
- 명백하게도 모든 이벤트가 10개의 리스너 제한이 필요한 것은 아니다.
- emitter.setMaxListeners() 메소드를 사용하면
특정 EventEmitter 인스턴스에 대해 그 제한을 변경할 수 있다.
Infinity (또는 0)을 지정하면 리스너를 숫자 제한 없이 등록할 수 있다.
- EventEmitter의 참조를 반환하기 때문에 연속해서 호출하는 것이 가능하다.
'web > javascript' 카테고리의 다른 글
date time random (0) | 2019.07.26 |
---|---|
Node.js - Http Module (0) | 2019.07.24 |
Node.js-Express (0) | 2019.07.22 |
Node.js (0) | 2019.07.21 |
Fullscreen (0) | 2019.07.19 |
댓글