eventize-js v1.1.1
!!! THIS IS THE DEVELOPMENT BRANCH - ALL STUFF HERE IS BY DEFAULT UNSTABLE - SO PLEASE USE IT WITH CARE !!!
eventize.js
A tiny and clever framework for synchronous event-driven programming in javascript.
yes, read correctly: the emitters call the listeners here synchronously and not asynchronously, as is the case with node.js events, for example
This is perfectly reasonable: sometimes you want to have control over when something happens. e.g., when your code runs inside an animation frame. Or you want to release resources immediately and instantaneously.
FEATURES
- all API calls and downstream-listener-calls are 100% synchronous :boom: no async! :stuck_out_tongue_closed_eyes:
- :sparkles: wildcards & priorities :exclamation:
- :rocket: smart api (based upon node.js events but in a pretty extended way)
- has typescript types included (well, in fact, it is written in typescript) :tada:
- supports all major browsers and Node.js environments
- very small footsprint ~2.3k gzip'd
- no runtime dependencies
- Apache-2.0 license
Installation
All you need is to install the package:
$ npm install eventize.js@2.0.0-beta
Getting Started
The underlying concept is simple: certain kinds of objects (called "emitters") emit named events that cause function "listeners" to be called.
Emitter
Every object can become an emitter; for this, the object must inject the Eventize API.
import eventize, {Eventize} from 'eventize-js';
const myObj = eventize({});
or, if you are more familiar with class-based objects
import {Eventize} from 'eventize-js';
class Foo extends Eventize {
// constructor() {
// super();
// }
}
const myOtherObj = new Foo();
Listener
Any function can be used as a listener. However, you can also use an object that defines methods that have the exact name of the given event.
myObj.on('foo', (bar) => {
console.log('I am a listener function and you called me with bar=', bar);
})
myObj.on('foo', {
foo(bar, plah) {
console.log('I am a method and you called me with bar=', bar, 'and plah=', plah);
}
})
Named Events
An emitter can emit any event name; parameters are optional
myObj.emit('bar'); // well, nothing happens here
myObj.emit('foo', 123, 456);
// => "I am a listener function and you called me with bar= 123"
// => "I am a method and you called me with bar= 123 and plah= 456"
If an emitter emits an event to which no listeners are attached, nothing happens.
Btw. an event name can be either a string or a symbol
API
How to Emitter
There are several ways to convert any object into an emitter.
Probably the most common method is to simply use eventize( myObj )
; this corresponds to the inject variant:
inject
eventize.inject( myObj ) // => myObj
Returns the same object, with the eventize api attached, by modifying the original object.
To create an emitter without modifying the original object, you can use the extend variant:
extend
eventize.extend( myObj ) // => myEventizedObj
Returns a new object, with the Eventize API attached. The original object is not modified here, instead the prototype of the new object is set to the original object.
For this purpose Object.create()
is used internally.
Class-based inheritance
The class-based approach is essentially the same as the extend method, but differs in its usage:
import {Eventize} from 'eventize-js';
class Foo extends Eventize {
// constructor() {
// super();
// }
}
Class-based, without inheritance
If you want to create an emitter class-based, but not via inheritance, you can also do this with the eventize method in the constructor, here as a typescript example:
import eventize, {Eventize} from 'eventize-js';
interface Foo extends Eventize {}
class Foo {
constructor() {
eventize(this);
}
}
The Emitter Eventize API
Each emitter object provides an API for subscribing, unsubscribing and emitting events. This API is called the Eventize API (because "Emitter Eventize API" is a bit too long and cumbersome).
method | description |
---|---|
.on( .. ) | subscribe to events |
.once( .. ) | subscribe to only the next event |
.off( .. ) | unsubscribe listeners |
.retain( .. ) | hold the last event until a subscriber gets it |
.emit( .. ) | emit an event |
These methods are explained in detail below:
How to listen
.on( .. )
The simplest and most direct way is to subscribe to an event using a function:
import eventize from 'eventize-js'
const myObj = eventize({});
const unsubscribe = myObj.on('myEventName', (arg1, arg2) => {
console.log('myEventName, arg1=', arg1, 'arg2=', arg2);
})
The listener function is called when the named event is emitted. The parameters of the listener function are optional and will be filled with the event parameters later (if there are any).
The return value of on()
is always the inverse of the call — the unsubscription of the listener.
Wildcards
If not only a specific named event should be reacted to, but all events, the catch-em-all wildcard event *
can be used:
myObj.on('*', (...args) => console.log('an event occured, args=', ...args))
If you want, you can simply omit the wildcard event:
myObj.on((...args) => console.log('an event occured, args=', ...args))
Multiple event names
Instead of a wildcard, you can also specify multiple event names:
myObj.on(['foo', 'bar'], (...args) => console.log('foo or bar occured, args=', ...args))
Priorities
Sometimes you also want to control the order in which the listeners are called.
By default, the listeners are called in the order in which they were subscribed — in their priority group; a priority group is defined by a number, where the default priority group is 0
and large numbers take precedence over small ones.
myObj.on('foo', () => console.log("I don't care when I am called"));
myObj.on('foo', -999, () => console.log("I would like to be the last in line"));
myObj.on(Number.MAX_VALUE, () => console.log("I will be the first"));
myObj.emit('foo');
// => "I will be the first"
// => "I don't care when I am called"
// => "I would like to be the last in line"
Listener objects
You can also use a listener object instead of a function:
myObj.on('foo', {
foo(...args) {
console.log('foo called with args=', ...args);
}
})
This is quite useful in conjunction with wildcards:
const Init = Symbol('init'); // yes, symbols are used here as event names
const Render = Symbol('render');
const Dispose = Symbol('dispose');
myObj.on({
[Init]() {
// initialize
}
[Render]() {
// show something
}
[Dispose]() {
// dispose resources
}
})
.. or multiple event names:
myObj.on(['init', 'dispose'], {
init() {
// initialize
}
goWild() {
// will probably not be called
}
dispose()) {
// dispose resources
}
})
Of course, this also works with priorities:
myObj.on(1000, {
foo() {
console.log('foo!');
}
bar() {
console.log('bar!');
}
})
As a last option it is also possible to pass the listener method as name or function to be called in addition to the listener object.
Named listener object method
myObj.on('hello', 'say', {
say(hello) {
console.log('hello', hello);
}
})
myObj.emit('hello', 'world');
// => "hello world"
Listener function with explicit context
myObj.on(
'hello',
function() {
console.log('hello', this.receiver);
}, {
receiver: 'world'
});
myObj.emit('hello');
// => "hello world"
Complete on() method signature overview
Finally, here is an overview of all possible call signatures of the .on( .. )
method:
.on( eventName*, [ priority, ] listenerFunc [, listenerObject] )
.on( eventName*, [ priority, ] listenerFuncName, listenerObject )
.on( eventName*, [ priority, ] listenerObject )
Additional shortcuts for the wildcard *
syntax:
.on( [ priority, ] listenerFunc [, listenerObject] )
.on( [ priority, ] listenerObject )
Legend
argument | type |
---|---|
eventName* | eventName or eventName[] |
eventName | string or symbol |
listenerFunc | function |
listenerFuncName | string or symbol |
listenerObject | object |
.once( .. )
.once()
does exactly the same as .on()
. the difference is: after the listener is called, it is automatically unsubscribed, so the listener method is only called exactly once.
myObj.once('hi', () => console.log('hello'));
myObj.emit('hi');
// => "hello"
myObj.emit('hi');
// => (nothing happens here)
.off( .. )
TODO
How to emit events
.emit( .. )
TODO
.retain( .. )
TODO
OLD DOCUMENTATION:
Getting Started
Attach the eventizer api to any javascript object you want.
import eventize from 'eventize-js';
const say = hello => world => console.log(hello, world);
const obj = eventize({});
obj.on('foo', say('hello'));
obj.once(['foo', 'bar'], Priority.AAA, {
foo: say('hej'),
});
obj.on(['foo', 'bar'], Priority.Low, say('moin moin'))
obj.emit('foo', 'world');
// => "hej world"
// => "hello world"
// => "moin moin world"
obj.on('foo', () => obj.off('foo'));
obj.emit(['foo', 'bar'], 'eventize');
// => "hello eventize"
// => "moin moin eventize"
API Reference
The eventize API
eventize( obj ) // alias for eventize.inject()
eventize.inject( obj ) // => eventizer === obj
eventize.extend( obj ) // => eventizer (prototype is obj)
eventize.create( obj ) // => eventizer
.. or if you like a more class based approach ..
import {Eventize} from 'eventize-js';
class Foo extends Eventize {
// foo has now the eventize superpowers!
}
The eventizer API
.on( eventName*, [ priority, ] listenerFunc [, listenerObject] )
.on( eventName*, [ priority, ] listenerFuncName, listenerObject )
.on( eventName*, [ priority, ] listenerObject )
.on( [ priority, ] listenerFunc [, listenerObject] ) => listenerObject.on( '*', listenerFunc )
.on( [ priority, ] listenerObject ) => listenerObject.on( '*', listenerObject )
eventName*: eventName | Array<eventName>
eventName: string
listenerFunc: function
listenerFuncName: string
listenerObject: object
eventizer.once( ... )
eventizer.emit( eventName* [, args... ] )
eventizer.off( listenerFunc [, listenerObject] )
eventizer.off( listenerFuncName, listenerObject )
eventizer.off( listenerObject )
eventizer.off( eventName )
eventizer.off()
retain()
eventizer.retain( eventName* )
Additional API Helpers
eventize.is( obj )
Check if obj
is an eventizer (object has the eventizer api implemented). Returns true
or false
eventize.Priority.Max
eventize.Priority.AAA
eventize.Priority.BB
eventize.Priority.C
eventize.Priority.Default = 0
eventize.Priority.Low
eventize.Priority.Min
Some predefined priorities. Use it or not. They are defined just for convenience.
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
8 years ago
8 years ago
8 years ago
8 years ago