Skip to content
Alexey Toksarov edited this page Jul 19, 2015 · 37 revisions

How do I get the latest value of a Property?

There is no getLatestValue method available and will not be either. You get the value by subscribing to the stream/property (using onValue) and handling the values in your callback. If you need the value of more than one source, use one of the combine methods.

Bacon.js works so that Properties and EventStreams connect to their sources (such as jQuery events) when they have at least one listener. They also automatically disconnect when there are no listeners. So, subscribing to the stream or property is important also from this aspect.

Why isn't my Property updated?

You have a situation like this:

var bus = new Bacon.Bus;
var property = bus.toProperty('FIRST');
bus.push('SECOND');
property.onValue(function(val) {
  console.log(val);
});

You'd like the console to log 'SECOND' but it logs 'FIRST' instead.

What's happening here is that your Property won't get updated when there are no listeners. Before you add an actual listener using onValue, the Property is not active; it's not listening to the underlying Bus. It ignores input until someone's interested. So it all boils down to the "laziness" of all Bacon streams and properties. This is a key feature too: the streams automatically "plug in" to their sources when a subscriber is added, and "unplug" when there are no more subscribers.

You may consider using a Bacon.Model instead. That'll give you a "settable property" with a bunch of additional features.

Why isn't my subscriber called?

There's a catch in using Bacon.fromArray (as well as Bacon.once and other synchronously responding sources): if you attach multiple subscribers, it will spit all of its contents to the first one and the latter subscribers will never be called. So, if you do for example this:

$(function() {
  var stream = Bacon.fromArray([1,2,new Bacon.Error("oops")])
  stream.onValue(function(x) {
      console.log("value", x)
  })
  stream.onError(function(x) {
      console.log("error", x)
  })
});

... the errors won't be logged. In real applications, you probably won't be using fromArray sources this way, but if you just want a workaround, you can change fromArray(x) to sequentially(0, x) to get an asynchronous source that'll handle multiple subscribers properly.

You wrap any stream as asynchronous by using stream.delay(0) too, but here's the catch that Error events aren't delayed.

Check you this fiddle and this issue for details.

Does bacon.js support synchronized/atomic updates of Properties?

Yes! Added in v0.4.0. See Issue #84 for more details.

Assume you have properties A and B and a property C = A + B. When the value of A changes from a1 to a2 and B changes from b1 to b2 simultaneously, you'd like C to update atomically so that it would go straight from a1+b1 to a2+b2. And it indeed does, starting from 0.4.0.

How does FRP / Bacon.js work on large scale?

So far my impression is that you can use FRP on both the small scale and the large, but that it is not a magic bullet for solving your problems. You'll still have to think about application architecture or you'll run into similar problems (spaghetti code) as with other paradigms.

You might consider applying an MVC-style architecture to your application and use Bacon.js streams and properties for communication between components. Your Models may be Streams, Properties or more complex objects exposing Streams, Properties and Buses. Have a look at one of my blog posts on TodoMVC with Bacon.js for some more information on this.

What do I lose when I use FRP? What are the cons?

It's up to you. You may apply FRP to solve some isolated problems in your application and lose nothing. Or you may model your application in a new way. If you, for instance, ditch MVF frameworks and use Bacon+jQuery instead, you'll get a somewhat simpler solution, less indirection and better composability. Then again, you'll lose the specific benefits of the framework. For instance, Backbone Collections are great and if you master them, you don't necessarily want to lose them. The good news is that you don't have to throw good things away. Bacon.js is not a framework, so it plays ball with frameworks. Have a look at how @pyykkis combined Bacon with Backbone collections. Please use any combination you like and tell me how you did it and how did you succeed :)

How stable is it? Is it ready for production?

It's been used in production for year by now. Lots of bugs have been fixed with the help of the open source community on the way.

Isn't it slow? How is the performance?

There are no benchmarks that would compare a large-scale Bacon.js solution to something else as of yet. Neither have I ever heard of anyone experiencing Bacon.js related performance problems. The system is not in fact very complicated, so you shouldn't experience major overhead in comparison with, for instance, just using jQuery events. In my Worzone game I shamelessly throw events around for every little thing that happens in the game (for instance, when a bullet moves a pixel), and according to my profiling in Chrome, the Bacon.js dispatching mechanism doesn't really show in the results. But that's hardly a real performance test. I didn't experience notable performance gain or loss when I converted the game from RxJs either. So, for me, it looks like perf's o.k.

There'a a related Issue related to performance testing.

How is the learning curve of Bacon.js?

Stepping into FRP from imperative programming is a big change, and will definitely take some time to grasp. However, if you're familiar with FP list handling (map, filter, flatMap...), you'll find similarities. I've had a couple of hands-on Bacon.js workshops so far and every single participant was able to complete at least a couple of features on their own (well, I gave some hints for sure).

Try it out and tell me.

How do I integrate Bacon.js with X?

Currently, Bacon.js supports creating EventStreams from jQuery events, DOM EventTarget, Node.js EventEmitter, jQuery Deferred, or any polling function. Have a look at the readme.

If you need to plug in other sources, creating an EventStream yourself isn't actually very hard. Have a look at the source code where all of these converters are implemented for reference. Also, read my blog post.

One option is always to create a Bus and just push events into that:

var bus = new Bacon.Bus()
bus.push("event")

I like coffeescript more than JS, but notice all your examples are in JS. Would I be better off just using JS with Bacon?

Bacon.js itself is written in CoffeeScript, which is better-suited for functional programming than Javascript, because of the nicer lambda syntax. Go ahead with CoffeeScript, should work fine. The examples are in JavaScript because that's the lowest common denominator; everyone reads JS.

How can I integrate bacon to my (coffeescript/haxe/dart/etc) code?

Just like you'd use any other Javascript library.

Bacon.js is a CoffeeScript library that's build into Javascript using Grunt. You can use one of the built js files as you like, or you can use it as a dependency in your Node.js, Yeoman or Bower project. See readme.

What's the difference to RxJs?

Bacon.js is inspired by RxJs and has similar concepts. The main difference in the design is the existence of two flavors of Observables: EventStream and Property, each of which have clearly defined semantics. The RxJs Observable does not tie the semantics as tightly. For instance, in RxJs there are "hot" and "cold" observables that behave differently even though the expose the same Observable interface.

Also, Bacon.js is fully open-source and has (arguably) better documentation.

Doesn't this turn "callback" hell into "mapfiltercombine" hell? How I will integrate a new developer to my FRP code?

What FRP does to callback hell is the same thing that FP does to for-loops. Some may argue that for-loops are easier to read than a map-filter-combo. It's the same thing with all new tools. Some managers fear that their employees will never learn Scala, so everything has to be written in Java. Would you still like to write code in Cobol?

What if I have multiple Frames in my application?

Bacon.js used instanceof checks and has some internal state that relies on the assumption that there's only one instance of the "Bacon machine".

Use a single instance of Bacon and share it between your frames. Like window.Bacon = window.parent.Bacon. See related issue

Is jQuery required?

Nope. Bacon.js adds an asEventStream to JQuery objects if it finds JQuery. But you can certainly use it without jQuery.

Clone this wiki locally