futoin-asyncevent

2.3.7 • Public • Published

NPM Version NPM Downloads Build Status stable

NPM

Stability: 3 - Stable

About

Implementation of a well-known event emitter pattern, but with fundamental requirement: execute all events asynchronously - there must be no emitter functionality function frames on the stack.

All other event emitters implementations are synchronous - they call handlers when event is emitted. As there is a known security-related timing problem of setTimeout() calls in browser, enhanced event loop is used from futoin-asyncsteps module.

Second important feature - strict check of allowed event types.

Documentation --> FutoIn Guide

Reference implementation of:

FTN15: Native Event API
Version: 1.1

Spec: FTN15: Native Event API v1.x

Author: Andrey Galkin

Extra details

  1. ActiveAsyncTool from AsyncSteps is used for each handler.
    • All exceptions can be traced runtime-defined way - exceptions are forced to be re-thrown in dedicated event loop call
    • Performance of setImmediate() with workaround for security-related slowdown in browsers.
    • A single immediate is used for regular execution of event.
  2. EventEmitter instance is hidden in target[EventEmitter.SYM_EVENT_EMITTER] property.
    • Almost no pollution to target object
    • Very fast lookup
    • on(), off(), once() and emit() are defined as properties which proxy call
  3. At the moment, emit() uses ES6 spread oprerator (e.g. ...args):
    • the approach which is around 4 times faster in Node.js compared to old ES5 browsers
    • there are no optimizations done yet (no significant case so far)
  4. Each event has own "on" and "once" arrays:
    • performance over memory tradeoff
    • "once" array is simply discarded and replaced after first use, if there are any handlers
    • on()/once() calls are cheap
    • off() call uses array#filter()
    • the same handler can be added more than once, but it gets removed on first off() call
    • off() removes handler both from "on" and "once" arrays
  5. Async event dispatch considerations:
    • once( 'event', () => o.once( 'event', ... ) ) approach IS NOT SAFE as it leads to missed events!
    • avoid emitting too many events in a synchronous loop - event handlers get scheduled, but not executed!

Installation for Node.js

Command line:

$ npm install futoin-asyncevent --save

or

$ yarn add futoin-asyncevent

Hint: checkout FutoIn CID for all tools setup.

Browser installation

Pre-built ES5 CJS modules are available under es5/. These modules can be used with webpack without transpiler - default "browser" entry point points to ES5 version.

Webpack dists are also available under dist/ folder, but their usage should be limited to sites without build process.

Warning: older browsers should use dist/polyfill-asyncevent.js for Symbol.

The following globals are available:

  • $asyncevent - global reference to futoin-asyncevent module
  • futoin - global namespace-like object for name clashing cases
  • futoin.$asyncevent - another reference to futoin-asyncevent module
  • futoin.EventEmitter - global reference to futoin-asyncevent.EventEmitter class

Examples

Simple steps

const $asyncevent = require('futoin-asyncevent');

class FirstClass {
    constructor() {
        // dynamically extend & pre-configure allowed events
        $asyncevent(this, ['event_one', 'event_two', 'event_three']);
    }
    
    someFunc() {
        this.emit( 'event_one', 'some_arg', 2, true );
    }
}

const o = new FirstClass();
const h = (a, b, c) => console.log(a, b, c);

// Basic event operation
// ---------------------
o.on('event_one', h);
o.off('event_one', h);
o.once('event_two', () => console.log('Second'));
o.someFunc();

// Advanced
// --------

// instanceof hook (ES6)
(o instanceof $asyncevent.EventEmitter) === true

// update max listeners warning threshold
$asyncevent.EventEmitter.setMaxListeners( o, 16 );

// Additional events in derived class
// ----------------------------------
class DerivedClass extends FirstClass {
    constructor() {
        super();
        // Transparently checks, if EventEmitter has been already
        // registered with other events
        $asyncevent(this, ['another_event']);
    }
}

// Fail on duplicate event names
// ----------------------------------
class FailClass extends FirstClass {
    constructor() {
        super();
        // It's not allowed to override already registered event for
        // safety reasons.
        $asyncevent(this, ['event_one']);
    }
}

API documentation

The concept is described in FutoIn specification: FTN15: Native Event API v1.x

Classes

EventEmitter

Asynchronous Event Emitter.

Members

$asyncevent

window.$asyncevent - browser-only reference to futoin-asyncsteps module

$asyncevent

window.FutoIn.$asyncevent - browser-only reference to futoin-asyncsteps module

EventEmitter

window.futoin.EventEmitter - browser-only reference to futoin-asyncsteps.EventEmitter

Constants

$asyncevent

Attach event emitter properties to object. Call it in c-tor.

EventEmitter

Asynchronous Event Emitter.

Kind: global class
Note: Please avoid inheriting it, use EventEmitter.attach() instead!

eventEmitter.on(evt, handler)

Attach event handler.

Kind: instance method of EventEmitter

Param Type Description
evt string preconfigured event name
handler callable async event handler

eventEmitter.once(evt, handler)

Attach once-only event handler.

Kind: instance method of EventEmitter

Param Type Description
evt string preconfigured event name
handler callable async event handler

eventEmitter.off(evt, handler)

Remove event handler.

Kind: instance method of EventEmitter

Param Type Description
evt string preconfigured event name
handler callable async event handler

eventEmitter.emit(evt)

Emit async event.

Kind: instance method of EventEmitter

Param Type Description
evt string event name

EventEmitter.attach(instance, allowed_events, max_listeners)

Attach event emitter to any instance

Kind: static method of EventEmitter

Param Type Default Description
instance object target object
allowed_events array list of allowed event names
max_listeners integer 8 maximum allowed handlers per event name

EventEmitter.setMaxListeners(instance, max_listeners)

Dynamically update max listener count

Kind: static method of EventEmitter

Param Type Description
instance object target object
max_listeners integer maximum allowed handlers per event name

$asyncevent

window.$asyncevent - browser-only reference to futoin-asyncsteps module

Kind: global variable

$asyncevent.EventEmitter

Reference to EventEmitter class

Kind: static property of $asyncevent

$asyncevent

window.FutoIn.$asyncevent - browser-only reference to futoin-asyncsteps module

Kind: global variable

$asyncevent.EventEmitter

Reference to EventEmitter class

Kind: static property of $asyncevent

EventEmitter

window.futoin.EventEmitter - browser-only reference to futoin-asyncsteps.EventEmitter

Kind: global variable

eventEmitter.on(evt, handler)

Attach event handler.

Kind: instance method of EventEmitter

Param Type Description
evt string preconfigured event name
handler callable async event handler

eventEmitter.once(evt, handler)

Attach once-only event handler.

Kind: instance method of EventEmitter

Param Type Description
evt string preconfigured event name
handler callable async event handler

eventEmitter.off(evt, handler)

Remove event handler.

Kind: instance method of EventEmitter

Param Type Description
evt string preconfigured event name
handler callable async event handler

eventEmitter.emit(evt)

Emit async event.

Kind: instance method of EventEmitter

Param Type Description
evt string event name

EventEmitter.attach(instance, allowed_events, max_listeners)

Attach event emitter to any instance

Kind: static method of EventEmitter

Param Type Default Description
instance object target object
allowed_events array list of allowed event names
max_listeners integer 8 maximum allowed handlers per event name

EventEmitter.setMaxListeners(instance, max_listeners)

Dynamically update max listener count

Kind: static method of EventEmitter

Param Type Description
instance object target object
max_listeners integer maximum allowed handlers per event name

$asyncevent

Attach event emitter properties to object. Call it in c-tor.

Kind: global constant
See: EventEmitter.attach()

$asyncevent.EventEmitter

Reference to EventEmitter class

Kind: static property of $asyncevent

documented by jsdoc-to-markdown.

Package Sidebar

Install

npm i futoin-asyncevent

Weekly Downloads

2

Version

2.3.7

License

Apache-2.0

Unpacked Size

181 kB

Total Files

19

Last publish

Collaborators

  • andvgal