1

I have a Node.js app that represents a class room. I'm using Node version 11.11.0. This app has two files: index.js and student.js. The relevant code looks like this:

index.js

const EventEmitter = require('events');
const Student = require('./student');

async function start() {
  eventEmitter.on('newListener', (event, listener) => {
     console.log(`Added ${event} listener.`);
  });

  eventEmitter.on('handRaised', (question) => {
    console.log(question);
  });

  for (let i=0; i<10; i++) {
    let student = new Student(`Student #${i+1}`);
    student.attend();
  }
}

start();

student.js

'use strict';

const EventEmitter = require('events');

class Student {
  constructor(name) {
    this.name = name;
  }

  attend() {
    // simulate a student randomly asking a question within 10 minutes
    let minutes = (Math.floor(Math.random() * 10) + 1) * 60000;
    setTimeout(function() {
      EventEmitter.emit('handRaised', 'What is the 2 + 3?');
    }, minutes);
  }
}

module.exports = Student;

When I run this, I get an error that says EventEmitter.emit is not a function. I've tried several variations without any luck. What am I doing wrong?

Marcus
  • 1,097
  • 11
  • 21
Some User
  • 5,257
  • 13
  • 51
  • 93

2 Answers2

3

You can't use emit on EventEmitter directly. You need to have an instance of it. For eg.:

const eventEmitter = new EventEmitter();
// Now, you're okay to go with emit
eventEmitter.emit('handRaised', 'What is the 2 + 3?');

To use the same instance, define it in a file and require wherever you'll need it. Then, you're safe to use on and emit on it.

Bhojendra Rauniyar
  • 83,432
  • 35
  • 168
  • 231
0

The answer given by @Bhojendra Rauniyar is correct, imho, but is missing a working example which is given below. Note, a subtle, but important change I did to define the callback function for setTimeout() in student.js: I am using the arrow function () => which binds this of the student instance to the callback function. This is required to call the instance variable from the callback function. Alternatively function () { ... }.bind(this) can be used.

index.js

const EventEmitter = require('events');
const Student = require('./student');
const eventEmitter = new EventEmitter();

async function start() {
  eventEmitter.on('newListener', (event, listener) => {
    console.log(`Added ${event} listener.`);
});

  eventEmitter.on('handRaised', (question) => {
    console.log(question);
});

  for (let i=0; i<10; i++) {
    let student = new Student(`Student #${i+1}`, eventEmitter);
    student.attend();
  }
}

start();

student.js

'use strict';

class Student {
  constructor(name, eventEmitter) {
    this.name = name;
    this.eventEmitter = eventEmitter;
  }

  attend() {
    // simulate a student randomly asking a question within 10 minutes
    let minutes = (Math.floor(Math.random() * 10) + 1) * 60000;
    setTimeout(() => {
      this.eventEmitter.emit('handRaised', 'What is the 2 + 3?');
  }, minutes);
  }
}

module.exports = Student;
Marcus
  • 1,097
  • 11
  • 21
  • 1
    Awesome! I was missing the fact that I needed to pass the `eventEmitter` from the Classroom to the Student objects. I thought EventEmitter was some kind of global messaging system. – Some User Mar 17 '19 at 12:02
  • Another approach is to derive class "Student" from EventEmitter, and to let the managing code register a listener on each Student instance. See the following thread for an example https://stackoverflow.com/questions/38280844/use-eventemitter-in-es6-class – Marcus Mar 17 '19 at 12:50
  • @SomeUser for acheiving that you can use `Dependency Injection` pattern, and define a global `event manager` which extends `EventEmitter` and inject it in all of your classes which needs `EventEmitter` features. – MajidJafari Apr 26 '20 at 12:22