A framework to build your reactive library. It provides a basic feature set for your reactive library:
- asynchronous
- error handling
- back pressure
- operator extension
- source extension
The kernel of "bouton.js" is a "Node" or we can called it a "Bouton". See the following diagram: (Isn't it looks like a button? in french: "bouton")
It has a bidirectional data flow:
[signal flow] : push(signal) -> Node -> send(signal) -> push(signal) -> Node
[command flow] : Node <- pull(cmd) <- request(cmd) <- Node <- pull(cmd)
The signal/data flow is called "downstream", the command flow is called "upstream".
By chaining Node together you can build your stream like what RxJS, bacon.js and highland.js do.
bouton.js ships with a small set of operators and signal generator, and provides a very simple API for you to extend you own operator and signal source.
To understand how bouton.js works, see the tutorial: Implement a JavaScript reactive programming library
I haven't provided many operators in this library yet (In progress). The idea of this library is to let you customize the library on your own way. You can build your own reactive programming library with your own set of operators.
Node is the basic and only data structure in bouton.js. Both your data source and operator are inherited from it.
From Node's internal point of view: it handles the received signal with onReceive(), and send() signal to downstream node. It also handles the request made by downstream with onRequest(), and makes a request() to upstream.
From external point of view: You can push() a signal to a node, and observe the signal emitted by the node with observe(). You can make a request with pull(). And observe the request made by the node by using observe().
See document : Node
In browser
// fromDOMEvent is an active source
bouton.fromDOMEvent("click", document.getElementById("btn"))
.throttle(500)
.map(v => 1)
.scan(0, (a, b) => a + b) // count the clicks
.act(console.log);
In node.js
const bouton = require("bouton").default();
bouton.asList([1, 2, 3]) // asList is a passive source
.map(v => {
console.log(v);
return v;
})
.sink(); // back pressure, drive the stream
- build nodejs library
npm run build
- pack web modules
developer version version, will generate build/bouton.js
npm run webpack
production version, will generate build/bouton.min.js
npm run webpack-prod
More sources coming soon
A source with only one signal in it.
Turn an array to a signal source. It is a passive source : you need a sink operator to drive the stream
Works in browser. send DOM event to the stream. This is an active source, you don't need a sink to drive it.
More operators coming soon
Make side effect with the signal, send the original signal to the stream
Handle END signal
Handle errors occurred on upstream. use rethrow to resend signal/error to stream.
transform signal to another form
handle signal with a state
drive stream with a passive source
Only allow one signal pass in ms millisecond.
add a new operator. An operator is a function which returns a Node instance.
For example:
bouton.addOperator("count", (initCount) => {
class CountNode extends bouton.Node {
constructor(options, eventemitter) {
super(options, eventemitter);
this.initCount = this.options;
}
onSignal(signal) {
this.initCount++;
this.send(this.initCount);
}
}
return new CountNode(initCount);
});
bouton.asList(["a", "b", "c"])
.count(10)
.act(console.log)
.sink();
// output
// 11
// 12
// 13
add multiple operators, accept a map of name:operator pair as argument
register a source function.
register multiple sources. accept a map of name:source pair as argument
register the default operators and sources.
const bouton = require("bouton").default();