Skip to content

sskyy/roof-bus

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

61 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

npm version Build Status

roof-bus is a event bus for both practical programmers and application architects. It can be used as a simple string-based event bus whitout any buy-in in small apps. When handling large apps, especially the one with complex business logic, its powerful features like Listener Sequence Control can help you keep the system low-coupled and flexsible during the growth.

Quick Start

roof-bus is really easy to get start with, check the code below. I don't think it need any explaination.

var Bus = require('roof-bus')
var bus = new Bus

bus.on('start', function busListener( arg1, arg2 ){
	console.log('listener fired', arg1, arg2)
})

bus.fire('start', 'argument1', 'argument2')

Most event libraries use a simple first-in, first-out listener sequence. But sometimes simple tactic can not solve real-world problem. For example, framework usually load modules from file system in alphabetical order, but module's listener may require a diffrent registration order. Listener which control the access of certain functionality may require to be called first of the sequence.

With roof-node, you can do it easily. Say that we have two listeners in different file both listen to event start, but one of them is in charge of validating the arguments.

start-something.js:

module.exports = function(bus){
	bus.on('start', function( owner ){
		console.log(owner + 'started something')
	})
}

validate-arguments.js:

module.exports = function(bus){
	bus.on('start',{
		fn: function(owner){
			if(owner !== 'Jane') throw new Error('owner is not Jane')
		},
		first : true
	})
}

To put the validation listener to first place, simply change the listener to a object with attribute first that set to true. If you want to stop following listeners from being called, just throw a error.

Sometime to describe complex business flow, listeners need to fire another event, like:

bus.on('process1', function(){
	bus.fire('process2')
})

When the hierachy goes deep, it will be hard to figure out what exactly happend when top event fired. Don't worry, roof-bus generates a detailed tracestack every time. And already been used in a web framework with amazing

Read on for more usage, you may find more practical features.

Usage

1. Simple on and fire.

var Bus = require('roof-bus')
var bus = new Bus

bus.on('start', function busListener( arg1, arg2 ){
	console.log('listener fired', arg1, arg2)
})

bus.fire('start', 'argument1', 'argument2')

2. Listener Sequence Control

Order of listeners on the same event can be controlled. Just name you listener function and then use the function name in attribute before or after.

bus.on('start', function listener1( arg1, arg2 ){
	console.log('listener1 fired', arg1, arg2)
})

bus.on('start', {
	fn: function listener2( arg1, arg2 ){
		console.log('listener2 fired', arg1, arg2)
	},
	before : ['listener1']
})

bus.fire('start', 'argument1', 'argument2')

There are four order control attributes: before after first last. Check below.

bus.on('start', function listener1( arg1, arg2 ){
	console.log('listener1 fired', arg1, arg2)
})

bus.on('start', {
	fn: function listener2( arg1, arg2 ){
		console.log('listener2 fired', arg1, arg2)
	},
	before : ['listener1']
})

bus.on('start', {
	fn: function listener3( arg1, arg2 ){
		console.log('listener3 fired', arg1, arg2)
	},
	after : ['listener1']
})

bus.on('start', {
	fn: function listener4( arg1, arg2 ){
		console.log('listener4 fired', arg1, arg2)
	},
	first : true
})

bus.fire('start', 'argument1', 'argument2')
//fire order: 4 2 1 3

3. Handle asynchronicity

fire method always return a promise. If you have synchronous code in listener and want roof-bus wait for you, return a promise.

bus.on('start', function listener1( arg1, arg2 ){
	return new Promise(function(resolve,reject){
		setTimeout(resolve, 1000)
	})
})


bus.fire('start', 'argument1', 'argument2').then(function(){
	console.log('show in 1 second')
})

Note that listeners are fired synchronously as default. So listener which returns promise will block followers until promise resolve.

bus.on('start', function listener1( arg1, arg2 ){
	return new Promise(function(resolve,reject){
		setTimeout(resolve, 1000)
	})
})

bus.on('start', {
	fn: function listener2( arg1, arg2 ){
		console.log('will wait for listener1 resolve')
	},
	after : ['listener1']
})


bus.fire('start', 'argument1', 'argument2').then(function(){
	console.log('will wait all listener fired')
})

If you want some listeners to execute asynchronously, you can set async attribute to true as below.

bus.on('start', {
	fn: function listener1( arg1, arg2 ){
		return new Promise(function(resolve,reject){
			setTimeout(resolve, 1000)
		})
	},
	async : true
})

bus.on('start', {
	fn: function listener2( arg1, arg2 ){
		console.log('will not be blocked by listener1')
	},
	after : ['listener1']
})



bus.fire('start', 'argument1', 'argument2').then(function(){
	console.log('show in 1 second')
})

4. Generator support

If you have asynchrous code and do not like promise, you can use generator:

bus.on('start', function *listener1(){
	yield somethingAsync()
})

bus.on('start', {
    fn: function listener2( arg1, arg2 ){
        console.log('will be blocked by listener1')
    },
    after : ['listener1']
})

5. Passing data between listeners

Note the order of listeners is important when passing data.

bus.on('start', function listener1( arg1, arg2 ){
	this.data.set('name','Bill')
})

bus.on('start', {
	fn: function listener2( arg1, arg2 ){
		console.log(this.data.get('name'))     // 'Bill'
	},
	after : 'listener1'
})

6. Fire inside listener

You already we can fire another event inside listener, and roof-bus will keep a tracestack for you. Just keep one thing in mind that you must use this.fire inside the listener:

bus.on('topEvent', function(){
	this.fire('rootEvent')
})

7. Error handling

You can throw a build-in Error instance or use roof-bus error method.

bus.on('start', function listener1( arg1, arg2 ){
	throw new Error('some error')
	//or 
	//return this.error(500,{msg:'some error'})
})

bus.fire('start').then(function(){

}).catch(function( error){
	console.log( error)
})

Advanced

List events

It easy to get all registered events:

bus.on('sing', function(){})
bus.on('dance', function(){})

var events = bus.getEvents()

console.log( events )   //'sing' 'dance'

List listeners

Get all listeners for listened on certain event:

function listener1(){}
function listener2(){}
bus.on(event, listener1)
bus.on(event, {
	fn: listener2,
	before : 'listener1'
})

var listeners = bus.getListeners(event).toArray()
assert.equal( listeners[0].fn, listener2)
assert.equal( listeners[0].event, event)
assert.equal( listeners[1].fn, listener1)

Get listener tracestack

bus.on('dance', function danceListener(){
	this.data.set('name','Jane')
})

bus.fire('dance')

console.log( bus._runtime.data )

The tracestack structure:

{
	"event": {
		"name": "dance",
		"arguments": []
	},
	"listeners": {
		"danceListener": {
			"fn"    : [Function firstListener],
			"event" : "dance",
			"data"  :{
				"name":"Jane"
			}
		}
	}
}

Browse the test cases and examples for more detail. More documents coming soon.

About

cool event bus

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published