[![npm version](https://badge.fury.io/js/zero.png)](http://badge.fury.io/js/roof-bus)
[![Build Status](https://travis-ci.org/sskyy/roof-bus.svg?branch=master)](https://travis-ci.org/sskyy/roof-bus)

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.

```javascript
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:

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

validate-arguments.js:

```javascript
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:

```javascript
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 


<div style='text-align:center'>
	<img src='https://t.alipayobjects.com/images/rmsweb/T1WINjXddXXXXXXXXX.png' width='80%' />
</div>


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


## Usage

### 1. Simple `on` and `fire`.


```javascript
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`.

```javascript
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.

```javascript
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.

```javascript
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.

```javascript
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. 


```javascript
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:

```javascript
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.

```javascript
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:

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

```

### 7. Error handling

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

```javascript
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:

```javascript
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:

```javascript
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


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

bus.fire('dance')

console.log( bus._runtime.data )
```

The tracestack structure:

```javascript
{
	"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.