doc: improvements to events.markdown copy
authorJames M Snell <jasnell@gmail.com>
Tue, 29 Dec 2015 18:04:13 +0000 (10:04 -0800)
committerMyles Borins <mborins@us.ibm.com>
Tue, 19 Jan 2016 19:52:34 +0000 (11:52 -0800)
General improvements to events.markdown copy including a
bit of restructuring and improved examples

PR-URL: https://github.com/nodejs/node/pull/4468
Reviewed-By: Myles Borins <myles.borins@gmail.com>
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
doc/api/events.markdown

index 02617b1..0fdfc07 100644 (file)
 
 <!--type=module-->
 
-Many objects in Node.js emit events: a [`net.Server`][] emits an event each
-time a peer connects to it, a [`fs.ReadStream`][] emits an event when the file
-is opened. All objects which emit events are instances of `events.EventEmitter`.
-You can access this module by doing: `require("events");`
+Much of the Node.js core API is built around an idiomatic asynchronous
+event-driven architecture in which certain kinds of objects (called "emitters")
+periodically emit named events that cause Function objects ("listeners") to be
+called.
 
-Typically, event names are represented by a camel-cased string, however,
-there aren't any strict restrictions on that, as any valid property key will be
-accepted.
+For instance: a [`net.Server`][] object emits an event each time a peer
+connects to it; a [`fs.ReadStream`][] emits an event when the file is opened;
+a [stream][] emits an event whenever data is available to be read.
 
-Functions can then be attached to objects, to be executed when an event
-is emitted. These functions are called _listeners_. Inside a listener
-function, `this` refers to the `EventEmitter` that the listener was
-attached to.
+All objects that emit events are instances of the `EventEmitter` class. These
+objects expose an `eventEmitter.on()` function that allows one or more
+Functions to be attached to named events emitted by the object. Typically,
+event names are camel-cased strings but any valid JavaScript property key
+can be used.
 
+When the `EventEmitter` object emits an event, all of the Functions attached
+to that specific event are called _synchronously_. Any values returned by the
+called listeners are _ignored_ and will be discarded.
 
-## Class: events.EventEmitter
+The following example shows a simple `EventEmitter` instance with a single
+listener. The `eventEmitter.on()` method is used to register listeners, while
+the `eventEmitter.emit()` method is used to trigger the event.
 
-Use `require('events')` to access the EventEmitter class.
+    const EventEmitter = require('events');
+    const util = require('util');
+
+    function MyEmitter() {
+      EventEmitter.call(this);
+    }
+    util.inherits(MyEmitter, EventEmitter);
 
-```javascript
-const EventEmitter = require('events');
-```
+    const myEmitter = new MyEmitter();
+    myEmitter.on('event', function() {
+      console.log('an event occurred!');
+    });
+    myEmitter.emit('event');
 
-When an `EventEmitter` instance experiences an error, the typical action is
-to emit an `'error'` event.  Error events are treated as a special case in
-Node.js.  If there is no listener for it, then the default action is to print
-a stack trace and exit the program.
+Any object can become an `EventEmitter` through inheritance. The example above
+uses the traditional Node.js style prototypical inheritance using
+the `util.inherits()` method. It is, however, possible to use ES6 classes as
+well:
 
-All EventEmitters emit the event `'newListener'` when new listeners are
-added and `'removeListener'` when a listener is removed.
+    const EventEmitter = require('events');
 
-### Inheriting from 'EventEmitter'
+    class MyEmitter extends EventEmitter {}
 
-Inheriting from `EventEmitter` is no different from inheriting from any other
-constructor function. For example:
+    const myEmitter = new MyEmitter();
+    myEmitter.on('event', function() {
+      console.log('an event occurred!');
+    });
+    myEmitter.emit('event');
+
+## Passing arguments and `this` to listeners
+
+The `eventEmitter.emit()` method allows an arbitrary set of arguments to be
+passed to the listener functions. It is important to keep in mind that when an
+ordinary listener function is called by the `EventEmitter`, the standard `this`
+keyword is intentionally set to reference the `EventEmitter` to which the
+listener is attached.
+
+    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');
 
-    'use strict';
-    const util = require('util');
-    const EventEmitter = require('events');
+It is possible to use ES6 Arrow Functions as listeners, however, when doing so,
+the `this` keyword will no longer reference the `EventEmitter` instance:
 
-    function MyEventEmitter() {
-      // Initialize necessary properties from `EventEmitter` in this instance
-      EventEmitter.call(this);
-    }
+    const myEmitter = new MyEmitter();
+    myEmitter.on('event', (a, b) => {
+      console.log(a, b, this);
+        // Prints: a b {}
+    });
+    myEmitter.emit('event', 'a', 'b');
+
+## Asynchronous vs. Synchronous
+
+The `EventListener` calls all listeners synchronously in the order in which
+they were registered. This is important to ensure the proper sequencing of
+events and to avoid race conditions or logic errors. When appropriate,
+listener functions can switch to an asynchronous mode of operation using
+the `setImmediate()` or `process.nextTick()` methods:
+
+    const myEmitter = new MyEmitter();
+    myEmitter.on('event', (a, b) => {
+      setImmediate(() => {
+        console.log('this happens asynchronously');
+      });
+    });
+    myEmitter.emit('event', 'a', 'b');
+
+## Handling events only once
+
+When a listener is registered using the `eventEmitter.on()` method, that
+listener will be invoked _every time_ the named event is emitted.
+
+    const myEmitter = new MyEmitter();
+    var m = 0;
+    myEmitter.on('event', () => {
+      console.log(++m);
+    });
+    myEmitter.emit('event');
+      // Prints: 1
+    myEmitter.emit('event');
+      // Prints: 2
+
+Using the `eventEmitter.once()` method, it is possible to register a listener
+that is immediately unregistered after it is called.
+
+    const myEmitter = new MyEmitter();
+    var m = 0;
+    myEmitter.once('event', () => {
+      console.log(++m);
+    });
+    myEmitter.emit('event');
+      // Prints: 1
+    myEmitter.emit('event');
+      // Ignored
+
+## Error events
+
+When an error occurs within an `EventEmitter` instance, the typical action is
+for an `'error'` event to be emitted. These are treated as a special case
+within Node.js.
+
+If an `EventEmitter` does _not_ have at least one listener registered for the
+`'error'` event, and an `'error'` event is emitted, the error is thrown, a
+stack trace is printed, and the Node.js process exits.
+
+    const myEmitter = new MyEmitter();
+    myEmitter.emit('error', new Error('whoops!'));
+      // Throws and crashes Node.js
+
+To guard against crashing the Node.js process, developers can either register
+a listener for the `process.on('uncaughtException')` event or use the
+[`domain`][] module (_Note, however, that the `domain` module has been
+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
+
+As a best practice, developers should always register listeners for the
+`'error'` event:
+
+    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
 
-    // Inherit functions from `EventEmitter`'s prototype
-    util.inherits(MyEventEmitter, EventEmitter);
+## Class: EventEmitter
 
-### Class Method: EventEmitter.listenerCount(emitter, event)
+The `EventEmitter` class is defined and exposed by the `events` module:
 
-    Stability: 0 - Deprecated: Use [emitter.listenerCount][] instead.
+    const EventEmitter = require('events');
 
-Returns the number of listeners for a given event.
+All EventEmitters emit the event `'newListener'` when new listeners are
+added and `'removeListener'` when a listener is removed.
 
 ### Event: 'newListener'
 
 * `event` {String|Symbol} The event name
 * `listener` {Function} The event handler function
 
-This event is emitted *before* a listener is added. When this event is
-triggered, the listener has not been added to the array of listeners for the
-`event`. Any listeners added to the event `name` in the newListener event
-callback will be added *before* the listener that is in the process of being
-added.
+The `EventEmitter` instance will emit it's own `'newListener'` event *before*
+a listener is added to it's internal array of listeners.
+
+Listeners registered for the `'newListener'` event will be passed the event
+name and a reference to the listener being added.
+
+The fact that the event is triggered before adding the listener has a subtle
+but important side effect: any *additional* listeners registered to the same
+`name` *within* the `'newListener'` callback will be inserted *before* the
+listener that is in the process of being added.
+
+    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
 
 ### Event: 'removeListener'
 
 * `event` {String|Symbol} The event name
 * `listener` {Function} The event handler function
 
-This event is emitted *after* a listener is removed.  When this event is
-triggered, the listener has been removed from the array of listeners for the
-`event`.
+The `'removeListener'` event is emitted *after* a listener is removed.
+
+### EventEmitter.listenerCount(emitter, event)
+
+    Stability: 0 - Deprecated: Use [`emitter.listenerCount()`][] instead.
+
+A class method that returns the number of listeners for the given `event`
+registered on the given `emitter`.
+
+    const myEmitter = new MyEmitter();
+    myEmitter.on('event', () => {});
+    myEmitter.on('event', () => {});
+    console.log(EventEmitter.listenerCount(myEmitter, 'event'));
+      // Prints: 2
 
 ### EventEmitter.defaultMaxListeners
 
-[`emitter.setMaxListeners(n)`][] sets the maximum on a per-instance basis.
-This class property lets you set it for *all* `EventEmitter` instances,
-current and future, effective immediately. Use with care.
+By default, a maximum of `10` listeners can be registered for any single
+event. This limit can be changed for individual `EventEmitter` instances
+using the [`emitter.setMaxListeners(n)`][] method. To change the default
+for *all* `EventEmitter` instances, the `EventEmitter.defaultMaxListeners`
+property can be used.
+
+Take caution when setting the `EventEmitter.defaultMaxListeners` because the
+change effects *all* `EventEmitter` instances, including those created before
+the change is made. However, calling [`emitter.setMaxListeners(n)`][] still has
+precedence over `EventEmitter.defaultMaxListeners`.
 
-Note that [`emitter.setMaxListeners(n)`][] still has precedence over
-`EventEmitter.defaultMaxListeners`.
+Note that this is not a hard limit. The `EventEmitter` instance will allow
+more listeners to be added but will output a trace warning to stderr indicating
+that a `possible EventEmitter memory leak` has been detected. For any single
+`EventEmitter`, the `emitter.getMaxListeners()` and `emitter.setMaxListeners()`
+methods can be used to temporarily avoid this warning:
+
+    emitter.setMaxListeners(emitter.getMaxListeners() + 1);
+    emitter.once('event', () => {
+      // do stuff
+      emitter.setMaxListeners(Math.max(emitter.getMaxListeners() - 1, 0));
+    });
 
 ### emitter.addListener(event, listener)
 
@@ -93,77 +262,72 @@ Alias for `emitter.on(event, listener)`.
 
 ### emitter.emit(event[, arg1][, arg2][, ...])
 
-Calls each of the listeners in order with the supplied arguments.
+Synchronously calls each of the listeners registered for `event`, in the order
+they were registered, passing the supplied arguments to each.
 
 Returns `true` if event had listeners, `false` otherwise.
 
 ### emitter.getMaxListeners()
 
-Returns the current max listener value for the emitter which is either set by
-[`emitter.setMaxListeners(n)`][] or defaults to
+Returns the current max listener value for the `EventEmitter` which is either
+set by [`emitter.setMaxListeners(n)`][] or defaults to
 [`EventEmitter.defaultMaxListeners`][].
 
-This can be useful to increment/decrement max listeners to avoid the warning
-while not being irresponsible and setting a too big number.
-
-    emitter.setMaxListeners(emitter.getMaxListeners() + 1);
-    emitter.once('event', () => {
-      // do stuff
-      emitter.setMaxListeners(Math.max(emitter.getMaxListeners() - 1, 0));
-    });
-
-### emitter.listenerCount(type)
+### emitter.listenerCount(event)
 
-* `type` {Value} The type of event
+* `event` {Value} The type of event
 
-Returns the number of listeners listening to the `type` of event.
+Returns the number of listeners listening to the `event` type.
 
 ### emitter.listeners(event)
 
-Returns a copy of the array of listeners for the specified event.
+Returns a copy of the array of listeners for the specified `event`.
 
     server.on('connection', (stream) => {
       console.log('someone connected!');
     });
-    console.log(util.inspect(server.listeners('connection'))); // [ [Function] ]
+    console.log(util.inspect(server.listeners('connection')));
+      // Prints: [ [Function] ]
 
 ### emitter.on(event, listener)
 
-Adds a listener to the end of the listeners array for the specified `event`.
-No checks are made to see if the `listener` has already been added. Multiple
-calls passing the same combination of `event` and `listener` will result in the
-`listener` being added multiple times.
+Adds the `listener` function to the end of the listeners array for the
+specified `event`. No checks are made to see if the `listener` has already
+been added. Multiple calls passing the same combination of `event` and
+`listener` will result in the `listener` being added, and called, multiple
+times.
 
     server.on('connection', (stream) => {
       console.log('someone connected!');
     });
 
-Returns emitter, so calls can be chained.
+Returns a reference to the `EventEmitter` so calls can be chained.
 
 ### emitter.once(event, listener)
 
-Adds a **one time** listener for the event. This listener is
-invoked only the next time the event is fired, after which
-it is removed.
+Adds a **one time** `listener` function for the `event`. This listener is
+invoked only the next time `event` is triggered, after which it is removed.
 
     server.once('connection', (stream) => {
       console.log('Ah, we have our first user!');
     });
 
-Returns emitter, so calls can be chained.
+Returns a reference to the `EventEmitter` so calls can be chained.
 
 ### emitter.removeAllListeners([event])
 
-Removes all listeners, or those of the specified event. It's not a good idea to
-remove listeners that were added elsewhere in the code, especially when it's on
-an emitter that you didn't create (e.g. sockets or file streams).
+Removes all listeners, or those of the specified `event`.
+
+Note that it is bad practice to remove listeners added elsewhere in the code,
+particularly when the `EventEmitter` instance was created by some other
+component or module (e.g. sockets or file streams).
 
-Returns emitter, so calls can be chained.
+Returns a reference to the `EventEmitter` so calls can be chained.
 
 ### emitter.removeListener(event, listener)
 
-Removes a listener from the listener array for the specified event.
-**Caution**: changes array indices in the listener array behind the listener.
+Removes the specified `listener` from the listener array for the specified
+`event`.
 
     var callback = function(stream) {
       console.log('someone connected!');
@@ -177,19 +341,29 @@ listener array. If any single listener has been added multiple times to the
 listener array for the specified `event`, then `removeListener` must be called
 multiple times to remove each instance.
 
-Returns emitter, so calls can be chained.
+Because listeners are managed using an internal array, calling this will
+change the position indices of any listener registered *after* the listener
+being removed. This will not impact the order in which listeners are called,
+but it will means that any copies of the listener array as returned by
+the `emitter.listeners()` method will need to be recreated.
+
+Returns a reference to the `EventEmitter` so calls can be chained.
 
 ### emitter.setMaxListeners(n)
 
-By default EventEmitters will print a warning if more than 10 listeners are
-added for a particular event. This is a useful default which helps finding
-memory leaks. Obviously not all Emitters should be limited to 10. This function
-allows that to be increased. Set to `Infinity` (or `0`) for unlimited.
+By default EventEmitters will print a warning if more than `10` listeners are
+added for a particular event. This is a useful default that helps finding
+memory leaks. Obviously, not all events should be limited to just 10 listeners.
+The `emitter.setMaxListeners()` method allows the limit to be modified for this
+specific `EventEmitter` instance. The value can be set to `Infinity` (or `0`)
+for to indicate an unlimited number of listeners.
 
-Returns emitter, so calls can be chained.
+Returns a reference to the `EventEmitter` so calls can be chained.
 
 [`net.Server`]: net.html#net_class_net_server
 [`fs.ReadStream`]: fs.html#fs_class_fs_readstream
 [`emitter.setMaxListeners(n)`]: #events_emitter_setmaxlisteners_n
 [`EventEmitter.defaultMaxListeners`]: #events_eventemitter_defaultmaxlisteners
-[emitter.listenerCount]: #events_emitter_listenercount_type
+[`emitter.listenerCount()`]: #events_emitter_listenercount_event
+[`domain`]: domain.html
+[stream]: stream.html