Skip to content

Channel based event bus with request/reply pattern, using promises. For node & browser.

License

Notifications You must be signed in to change notification settings

risq/transceiver

Repository files navigation

transceiver Travis build status Code Climate Test Coverage Dependency Status devDependency Status

Channel based event bus with request/reply pattern, using promises. For node & browser.

NPM

Usage

// 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().

Installation

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.

Logging

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 :

node.js

In browser, open console and type localStorage.debug='transceiver:*', then reload the page. Logs will be displayed in the browser console :

browser

Using without Promises

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.

API Reference

transceiver

.channel(String name)

Returns a channel by its name. Channel is automatically created if it doesn't exists.


.setPromise(Function PromiseConstructor)

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);

Shorthands

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}`);

channel

.request(String name [, args])

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);
.request(Array requests)

Shorthand for .requestArray(Array requests).

.request(Object requests)

Shorthand for .requestProps(Object requests).


.reply(String name, Function handler [, Object context])

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}`;
  });
.reply(Object handlers [, Object context])

Defines several request handlers at the same time.

transceiver.channel('users')
  .reply({
    getUser: this.getUser,
    deleteUser: this.deleteUser,
  });

.replyPromise(String name, Function handler [, Object context])

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);
    });
  });
.replyPromise(Object handlers [, Object context])

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);
    })
  });

.all(Array requests|Object requests)

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 !'));

.race(Array requests|Object requests)

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()).


.requestArray(Array requests|Object requests)

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 !'));

.requestProps(Array requests|Object requests)

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);
  });

.emit(String event [, args])

Emits an event with given arguments to the channel.

transceiver.channel('auth')
  .emit('login', this.userId);

.on(String event, Function handler)

Adds a event listener to the channel.

transceiver.channel('auth')
  .on('login', userId => {
    console.log(`User ${userId} just logged in.`);
  });

.once(String event, Function handler)

Adds a one-time event listener to the channel.

transceiver.channel('auth')
  .once('login', userId => {
    console.log(`User ${userId} just logged in.`);
  });
.once(String event)

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.`);
  });

.off(String event, Function handler)

Removes an already defined event listener to the channel.


.reset()

Removes all event listeners and request handlers of the channel.

About

Channel based event bus with request/reply pattern, using promises. For node & browser.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published