Unify your API and abstract time using Functional Reactive Programming (FRP) and Bacon.js
APIs are hard. Sometimes they can have you provide a callback, other times they return a promise or be synchronous. You can unify the usage of your API and abstract concepts like sync or async, by using the paradigm Functional Reactive Programming with the help of a implementation called Bacon.js.
The result of function calls will be reactive data types from Bacon.js. If you are unsure how to use Bacon or what FRP is, the Bacon.js README is very helpful and has a comprehensive introduction.
Works on Node.js aswell as in Browserify.
npm install bacon.decorate --save
bower install bacon.decorate
or by copying the raw contents of index.js.
Bacon.decorate
is a convenience and syntactic sugar for Bacon.js,
and as such, it requires Bacon.js to be included or required if using
browserify or similar.
var decorate = require('./');
var Q = require('q');
var API = {
// Async values using promises
get: decorate.promise(function (arg1, arg2) {
// Could be something like $.ajax aswell
var def = Q.defer();
setTimeout(function () {
def.resolve("Async?");
}, 1000);
return def.promise;
}),
// Async values using callbacks
getAnother: decorate.callback(function (a, b, callback) {
setTimeout(function () {
callback(a + ", " + b)
}, 1000);
}),
// Regular sync values
sync: decorate.value(function (a, b) {
return a + ", " + b;
}),
// Or using events
// This is an Event Stream of body content
// triggered on click
event: decorate.event('click', function () {
return document.body;
}, '.currentTarget.innerHTML')
};
By adding decorator to all methods, we can access them without thinking about if it's async or sync and we can easily combine them in different ways.
// Waiting for multiple values (example)
API.get('foo', 'bar')
.combine(m.getAnother('Foo', 'Bar'), function (a, b) {
return a + ": " + b;
})
.combine(m.sync('Baz', 'Qux'), function (a, b) {
return a + ", " + b;
})
.onValue(function (value) {
console.log('val', value);
//=> val Async?: Foo, Bar, Baz, Qux
});
Automaticly choose wrapping type based on type of value returned
from function. Only works on value type wrapping functions including:
event
, promise
, value
and array
.
This is useful when you return different types of data in a function.
var someAPI = decorate.autoValue(function(a) {
if (someCondintional) {
return 42;
}
if (Array.isArray(a)) {
return a;
}
if (someOtherConditional) {
// 'click' is passed as event type
// through second argument to autoValue
return document.body;
}
return $.ajax({
url: '/async/call/here?' + $.params(a)
});
}, 'click');
// Logs the result of an ajax call.
// No need to check for sync values
someApi({ id : 1 }).log();
Wraps async callback result from fn
as a reactive data type.
var test = decorators.callback(function (a, b, callback) {
callback(a + ", " + b);
});
test('Hello', 'World!').log();
//=> Prints: Hello, World!
Wraps async node style callback (with error as first argument)
result from fn
as a reactive data type.
var test = decorators.callback(function (callback) {
callback(new Error('Denied!'));
});
test().onError(function(err) {
console.log(err.message);
})
//=> Prints: Denied!
See Bacon.js .fromNodeCallback()
Wraps event returned from fn
on eventName
channel. You can
pass in an optional transform function to transform the event object
returned from the fn
function.
See Bacon.js .fromEventTarget()
Wraps promise returned from fn
as a reactive data type.
abort
defines if .abort
method should be called on promise
when all subscribers of event stream is removed.
Wraps primitive value returned from fn
as a reactive data type.
See Bacon.js .once()
Wraps an array returned from fn
as a reactive data type.
The result is an event stream that gives a value for each
of the entries in the array.
Wraps to an event stream that gives value returned from fn
every ms defined by interval
.
Wraps to an event stream that gives value for each item
every ms defined by interval
from an array returned from fn
.
Same as decorate.sequentially(interval, fn)
, but starting from first
item in the returned array when the end is reached, instead of ending.
Gives an event stream that gives a value returned from fn
after
delay
milliseconds.
Returns an event stream with values from polling fn
every interval
milliseconds.