Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/johngeorgewright/hot-press
Event lifecycle management in JavaScript
https://github.com/johngeorgewright/hot-press
event-hierarchy event-lifecycle pubsub
Last synced: 6 days ago
JSON representation
Event lifecycle management in JavaScript
- Host: GitHub
- URL: https://github.com/johngeorgewright/hot-press
- Owner: johngeorgewright
- License: mit
- Created: 2016-05-27T09:32:12.000Z (over 8 years ago)
- Default Branch: develop
- Last Pushed: 2020-06-22T17:51:24.000Z (over 4 years ago)
- Last Synced: 2024-11-10T00:44:38.271Z (about 2 months ago)
- Topics: event-hierarchy, event-lifecycle, pubsub
- Language: JavaScript
- Homepage:
- Size: 1.1 MB
- Stars: 1
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# Hot Press
[![Coverage Status](https://img.shields.io/coveralls/johngeorgewright/hot-press/master.svg?style=flat-square)](https://coveralls.io/github/johngeorgewright/hot-press?branch=master)
[![Build Status](https://img.shields.io/travis/johngeorgewright/hot-press/master.svg?style=flat-square)](https://travis-ci.org/johngeorgewright/hot-press)
[![NPM Version](https://img.shields.io/npm/v/hot-press.svg?style=flat-square)](https://www.npmjs.com/package/hot-press)
[![Greenkeeper badge](https://badges.greenkeeper.io/johngeorgewright/hot-press.svg)](https://greenkeeper.io/)
[![License](https://img.shields.io/npm/l/hot-press.svg?style=flat-square)](https://github.com/johngeorgewright/hot-press/blob/master/LICENSE)Hot Press is an event lifecycle management library for Node.js.
## Installation
```
npm i hot-press
```## Examples
### Standard PubSub
Basic PubSub architecture we're mostly familiar with. Using the `on()` function,
you can add subscribers to events that are published using the `emit()`
function.```javascript
import HotPress from 'hot-press';const {emit, on} = new HotPress();
on('event', (eventName, ...data) => console.log(...data));
emit('event', 'some', 'variables');
// 'some' 'variables'
```### Subscribing to multiple events
Using the `all` function you can trigger a subscriber only once all the events
have been emitted.```javascript
import HotPress from 'hot-press';const {all, emit} = new HotPress();
all({on: ['event1', 'event2']}, () => console.log('Triggered!'));
emit('event1');
emit('event2');
// 'Triggered!'
```### Subscription hierarchy
Dots symbolize subscription hierarchy. Using the `*` operator, you can subscribe
to all events under that hierarchy.```javascript
import HotPress from 'hot-press';const {emit, on} = new HotPress();
on('*', e => console.log(2, e));
on('e.*', e => console.log(1, e));
emit('e.f');
// 1 e.f
// 2 e.f
```### Unsubscribe
Remove all or specific subscribers from events using the `off()` function.
```javascript
import HotPress from 'hot-press';const {emit, off, on} = new HotPress();
const fn = () => console.log('blah');on('event', fn);
on('event', fn);
on('event', fn);emit('event');
// blah
// blah
// blahoff('event', fn);
emit('event');
// blah
// blahoff('event');
emit('event');
//
```### Subscribing to the beginning and end of events
There are 3 parts to an event lifecycle.
1. "Before" the event
2. "On" the event
3. "After" the eventYou can subscribe to any part of the event lifecycle using the appropriate
function.```javascript
import HotPress from 'hot-press';const {after, before, emit, off, on} = new HotPress();
before('event', () => console.log(1));
on('event', () => console.log(2));
after('event', () => console.log(3));
emit('event');
// 1
// 2
// 3
off('event');after('event', () => console.log(3));
before('event', () => console.log(1));
on('event', () => console.log(2));
emit('event');
// 1
// 2
// 3
```### Asynchronous subscriptions
If your subscription returns a Promise then the next part of the event lifecycle
will not be published until the promise has resolved.> **Note:** It will not delay subscriptions within the same part of the lifecycle,
> only those that are to be published next. For example, all subscriptions
> implemented with the `on()` function will wait until all subscriptions
> implemented with the `before()` function have been resolved.And finally the `emit` function will return a promise that will resolve once
the "after" part of the lifecycle has resolved.```javascript
import HotPress from 'hot-press';const {after, before, emit, on} = new HotPress();
before('event', eventuallyLog(1));
on('event', eventuallyLog(2));
after('event', eventuallyLog(3));
emit('event').then(() => console.log(4));
// 1
// 2
// 3
// 4function eventuallyLog(num) {
return () => new Promise(resolve => {
const log = () => {
console.log(num);
resolve();
};
setTimeout(log, random());
});
}function random(from=0, to=1000) {
return Math.floor(Math.random() * to) + from;
}
```#### Timeouts
There is a configurable timeout for these asynchronous events. By default it's
300ms, but it can be configured like so:```javascript
import HotPress from 'hot-press';const emitter = new HotPress();
emitter.timeout = 1000; // global
emitter.ns('myNamespace').timeout = 600; // specific to a namespace
```If the timeout is exceeded by a listener within any part of event lifecycle, the
listener is terminated and an error event is published; it will not kill the
event.### Namespacing
You can create a version of hot-press prefixing all messages with a namespace.
```javascript
import HotPress from 'hot-press';
import {strictEqual} from 'assert';const {ns} = new HotPress();
const foo = ns('foo');foo.on('event', eventName => console.log(eventName));
foo.emit('event');
// foo.event
```Namespaces can be retrieved uses the dot syntax, or chained `ns()` calls.
```javascript
import HotPress from 'hot-press';
import {strictEqual} from 'assert';const {ns} = new HotPress();
strictEqual(
ns('nested').ns('namespaces'),
ns('nested.namespaces'),
'Namespaces can be retrieved using dots as hierarchy, or chained calls to `ns`'
);
```Namespaces also cascade their settings.
```javascript
import HotPress from 'hot-press';
import {equal} from 'assert';const {ns} = new HotPress();
const foo = ns('foo');
foo.timeout = 1000;equal(
foo.timeout,
foo.ns('bar').timeout,
'Namespaces cascade their settings'
);
```### Error handling
Errors thrown within listeners/subscribers are swallowed but can be captured in
error events:```javascript
import HotPress from 'hot-press';const {emit, on} = new HotPress();
on('error.event', error => console.log(error.message));
on('event', () => throw new Error('Something went wrong'));
emit('event');
// "Something went wrong"
```### Procedures
Procedures are essentially functions, referenced by a string. There are a few
reasons you may want to use procedures:- Whether the procedure exists or not, you can call it without anything
failing. This is useful when using procedures in a plugable interface, when
you're unsure whether the procedure is going to be there or not.
- Hooking in to the function call. Every call to a procedure emits an event
with the same name as the procedure. The event lifecycle will exist, which
also means you can pause the call with the `before()` method.
- If you've decided to use HotPress as a basis to your framework/application
it can mean your API is more consistent.To register a procedure, use the `reg()` function. To cal your registered
procedure, use the `call()` method.If the procedure returns a promise, it will wait for the promise to resolve
before passing the resolved data back to the caller.The `call()` function always returns a promise.
```javascript
import User from '../lib/user';
import HotPress from 'hot-press';const {reg, call, on} = new HotPress();
reg('users.get', query => User.all(query));
on('error.users.get', error => console.error(error));
on('users.get', query => console.log('Querying users table', query));
call('users.get', {type: 'admin'}).then(users => {
// Do something with users
});
```## Custom Lifecycles
The `before()`, `on()` & `after()` methods don't suffice everyone's needs.
Luckily enough, for those people, the lifecycle is configurable.> **Note** you must still include an `on` method. Procedures rely on it.
```javascript
import HotPress from 'hot-press';const emitter = new HotPress();
emitter.lifecycle = ['foo', 'bar', 'on', 'after'];
const {foo, bar, on, after, emit} = emitter;foo('event', console.log);
bar('event', console.log);
on('event', console.log);
after('event', console.log);emit('event');
// foo
// bar
// on
// after
```The lifecycle is configurable per namespace too.
```javascript
import HotPress from 'hot-press';const {ns} = new HotPress();
const foo = ns('foo');
foo.lifecycle = ['prior', 'on', 'end'];foo.prior('event', console.log);
foo.on('event', console.log);
foo.end('event', console.log);
foo.emit('event');
// prior
// on
// end
```## Why the weird name?
Mostly because the good ones were already taken and there's arguably a relation
between publishing/subscribing to magazines and papers.## API
### `after(String eventName, Function subscriber)`
Register a subscribing function to the end of the event.
### `all(Object<[lifecycleName]: String[]> eventNames, Function subscriber)`
Register a subscriber for when all events have been published
### `call(String procedureName, [...Any]) ==> Promise`
Call a procedure with the given arguments.
### `dereg(String procedureName) ==> Number`
Deregisters a procedure.
### `deregAll() ==> Number`
Removes **all** registered procedures.
### `emit(String eventName, [Any ...data]) ==> Promise`
Publishes the event and passes all data arguments directly to the subscribers.
### `[lifecycleName](String eventName, Function subscriber)`
Register a subscribing function to that part of the event lifecycle.
### `ns(String namespace) ==> HotPress`
Creates an object containing the entire API of which all messages/event names
will be prefixed with a namespace.### `off(String eventName, [Function subscriber]) ==> Number`
Removes a given subscriber from an event. If none is specified, it will remove
all subscribers from the event.### `on(String eventName, Function subscriber)`
Register a subscribing function to the event
### `once(String eventName, Function subscriber)`
Registers a subscriber for just one event before it's removed.
### `once[LifecycleName](String eventName, Function subscriber)`
Registers a subscriber, to that part of the event lifecycle, for just one event
before it is removed.### `triggers(String eventName, Array eventNames)`
When the event has been published, publish an array of of other events.
### `triggers[lifecycleName](String eventName, Function subscriber)`
When the event's lifecycle part has been published, publish an array of other
events.### `reg(String procedureName, Function procedure)`
Registers a procedure.