Channel based event bus with request/reply pattern, using promises. For node & browser.
// app.js
const users = transceiver.channel('users');
const auth = transceiver.channel('auth');
const loader = transceiver.channel('loader');
auth.once('login')
.then(userId => users.request('getUser', userId))
.then(user => users.request('getUsername', user))
.then(username => console.log(`${username} just logged in !`))
.then(() => loader.all([
'loadAssets',
'loadSounds',
]))
.then(() => console.log('Assets loaded !'))
.then(() => transceiver.channel('app').emit('ready'));
// users.js
transceiver.channel('users')
.reply({
getUser,
getUsername: user => `User ${user.name}`,
});
function getUser(userId) {
return new Promise((resolve, reject) => {
// Retrieve user from db
// ...
resolve({name: 'bob'});
});
}
// loader.js
transceiver.channel('loader')
.replyPromise({
loadAssets: (resolve, reject) => setTimeout(resolve, 2000),
loadSounds: (resolve, reject) => setTimeout(resolve, 2500),
});
// auth.js
transceiver.channel('auth')
.emit('login', 42);
Channels can be accessed from everywhere using transceiver
module.
By default, making a request (using channel.request()
)
on a channel returns a Promise which is resolved by the specified handler (with channel.reply()
),
on the same channel.
transceiver
does not ship any Promise engine. It tries to use global Promise
object if available, but Promise constructor can be also set to a custom
library, like bluebird, or any
Promises/A+ implementation (see
transceiver.setPromise()
).
Promise usage can also be globally disabled. If so, methods will use classic callbacks to call handlers.
Every channel also implements EventEmitter
API which allows to use methods
on()
, emit()
,
once()
and off()
.
Use npm install --save transceiver
to get transceiver
.
// ES6/Babel
import transceiver from 'transceiver';
const authChannel = transceiver.channel('auth');
// Node.js / Browserify / requirejs...
var transceiver = require('transceiver');
var authChannel = transceiver.channel('auth');
transceiver
is written in ES6 and bundled as UMD/ES5 thanks to generator-babel-boilerplate
.
transceiver
uses debug
module for
environment agnostic logging. Logging can be useful to debug events and requests
calls, and to display potential warnings (e.g. in case of unhandled or
overwritten request).
With node.js, use DEBUG="transceiver:*"
environment variable to display logs.
Logs will be displayed in the terminal :
In browser, open console and type localStorage.debug='transceiver:*'
, then
reload the page. Logs will be displayed in the browser console :
If no global Promise object is defined when transceiver
initialize, it will
switch to regular callback mode.
Regular callback mode can also be forced at any time with the following command :
transceiver.setPromise(null);
In this mode, requests will return handler function result instead of a Promise object :
transceiver.channel('users')
.reply('getUsername', user => `User ${user.name}`);
const username = transceiver.channel('users')
.request('getUsername', myUser);
Async requests can be handled using callbacks :
transceiver.channel('users')
.reply('getUser', (userId, done) => {
// Retrieve user from db
// ...
done({name: 'bob'});
});
const username = transceiver.channel('users')
.request('getUser', 42, (user) => {
console.log(user);
});
Promise related methods channel.all()
, channel.race()
and channel.requestPromise()
will be unusable in this mode.
- transceiver
- channel
.request(String name [, args])
.request(Array requests)
.request(Object requests)
.reply(String name, Function handler [, Object context])
.reply(Object handlers [, Object context])
.replyPromise(String name, Function handler [, Object context])
.replyPromise(Object handlers [, Object context])
.all(Array requests|Object requests)
.race(Array requests|Object requests)
.requestArray(Array requests|Object requests)
.requestProps(Array requests|Object requests)
.emit(String event [, args])
.on(String event, Function handler)
.once(String event, Function handler)
.once(String event)
.off(String event, Function handler)
.reset()
Returns a channel by its name. Channel is automatically created if it doesn't exists.
Override the Promise constructor to another Promise engine. Use
setPromise(null)
to disable automatic promisification of callbacks. See
Using without Promise section for more information.
// Use a custom Promise library
transceiver.setPromise(Bluebird);
// Globally disable transceiver Promises usage
transceiver.setPromise(null);
Every method from a channel can be accessed directly via transceiver
using
transceiver.methodName(channelName, ...args)
:
transceiver.emit('auth', 'login');
transceiver.reply('users', 'getUsername', user => `User ${user.name}`);
Send a request to the channel. If defined, call the request handler with given arguments. The request handler will be automatically wrapped into a Promise if a global Promise constructor is defined.
transceiver.channel('users')
.reply('getUsername', userId => {
return `user #${userId}`;
});
transceiver.channel('users')
.request('getUsername', userId)
.then((username) => {
console.log(username);
});
To prevent it and call defined handler as a regular callback,
use transceiver.setPromise(null)
.
transceiver.setPromise(null);
transceiver.channel('users')
.reply('getUsername', userId => {
return `user #${userId}`;
});
const username = transceiver.channel('users')
.request('getUsername', userId);
Shorthand for .requestArray(Array requests)
.
Shorthand for .requestProps(Object requests)
.
Defines a new request handler for the channel. If a handler is already defined for the given request name, it will be overwritten.
If request handler does not return a Promise, it will be automatically wrapped into a Promise (only if a global Promise constructor is defined).
transceiver.channel('users')
.reply('getUsername', userId => {
return `user #${userId}`;
});
Defines several request handlers at the same time.
transceiver.channel('users')
.reply({
getUser: this.getUser,
deleteUser: this.deleteUser,
});
Shorthand for replying a new Promise. Uses defined Promise engine (the global
Promise constructor, if not overwritten by transceiver.setPromise()
).
transceiver.channel('loader')
.replyPromise('loadAssets', (resolve, reject) => {
setTimeout(resolve, 1000);
});
// Same as
transceiver.channel('loader')
.reply('loadAssets', () => {
return new Promise(resolve, reject) => {
setTimeout(resolve, 1000);
});
});
Shorthand for replying several new Promises. Uses defined Promise engine (the
global Promise constructor, if not overwritten by transceiver.setPromise()
).
transceiver.channel('loader')
.replyPromise({
loadAssets: (resolve, reject) => {
setTimeout(resolve, 1000);
}),
loadSounds: (resolve, reject) => {
setTimeout(resolve, 2000);
})
});
Returns a promise that resolves when every given requests are resolved. Passes the result as an array of every requests result.
Arguments can be passed for each request by using an object of requests instead of a simple array of request names.
// Using an array of requests
transceiver.channel('loader')
.all([
'loadImages',
'loadSounds',
'loadData',
])
.then(() => console.log('All assets have been loaded !'));
// Using an object of requests to pass arguments
transceiver.channel('loader')
.all({
loadImages: ['any', 'argument', 42], // Pass a list of arguments using an array as value
loadSounds: [],
loadData: true, // A single argument can be passed directly, if it is not an array
})
.then(() => console.log('All assets have been loaded !'));
Returns a promise that resolves when one of the given requests is resolved. Passes the result of the first resolved request.
Arguments can be passed for each request by using an object of requests instead
of a simple array of request names (same usage as channel.all()
).
Sends several requests at the same time, and returns handlers results as an array.
Arguments can be passed for each request by using an object of requests instead
of a simple array of request names (same usage as channel.all()
).
Note: If a Promise engine is used, the result will be an array of promises.
const promisesArray = transceiver.channel('loader')
.requestArray(['loadImages', 'loadSounds', 'loadData']);
Promise.all(promisesArray)
.then(() => console.log('All assets have been loaded !'));
// Same as
transceiver.channel('loader')
.all(['loadImages', 'loadSounds', 'loadData'])
.then(() => console.log('All assets have been loaded !'));
Sends several requests at the same time, and returns an object where keys correspond to requests names and values to their respective handlers results.
Arguments can be passed for each request by using an object of requests instead
of a simple array of request names (same usage as channel.all()
).
Note: If a Promise engine is used, the result will be an object of promises.
Can be useful with promise libraries which implements props()
method (like
bluebird).
import Bluebird from 'bluebird';
transceiver.channel('test')
.replyPromise({
req1: (resolve) => setTimeout(resolve, 2000, 'req1 result'),
req2: (resolve) => setTimeout(resolve, 1000, 'req2 result'),
});
const promisesAsProps = transceiver.channel('test')
.requestProps(['req1', 'req2']);
// Note: Arguments can be passed to the request handlers by using:
// .requestProps({req1: [args], req2: [args]});
Bluebird.props(promisesAsProps)
.then((res) => {
console.log(res.req1);
console.log(res.req2);
});
Emits an event with given arguments to the channel.
transceiver.channel('auth')
.emit('login', this.userId);
Adds a event listener to the channel.
transceiver.channel('auth')
.on('login', userId => {
console.log(`User ${userId} just logged in.`);
});
Adds a one-time event listener to the channel.
transceiver.channel('auth')
.once('login', userId => {
console.log(`User ${userId} just logged in.`);
});
Adds a one-time event listener to the channel. Returns a promise which resolves when the given event is emitted (only if a global Promise constructor is defined).
transceiver.channel('auth')
.once('login')
.then(userId => {
console.log(`User ${userId} just logged in.`);
});
Removes an already defined event listener to the channel.
Removes all event listeners and request handlers of the channel.