baconjs / bacon.js

Functional reactive programming library for TypeScript and JavaScript
https://baconjs.github.io
MIT License
6.47k stars 331 forks source link

What is lazy evaluation in Bacon.js? #660

Closed web3rover closed 8 years ago

web3rover commented 8 years ago

I am not able to understand what is lazy evaluation in Bacon.js.

I wrote the example provided by Bacon using map and flatMap and I get the same result.

Here is the HTML

<input id="itemname" type="text" />
<input id="additem" type="button" value="Add Item" />
<input id="purchase" type="button" value="Purchase" />

Here is the JS for code using map

var items = $("#additem").asEventStream("click").map(function(e){
    console.log("Executing");
    return document.getElementById("itemname").value;
}).toProperty();

var submittedItems = items.sampledBy($("#purchase").asEventStream("click"));

submittedItems.onValue(function(){})

Here is the JS for code using flatMap

var items = $("#additem").asEventStream("click").flatMap(function(e){
    console.log("Executing");
    return document.getElementById("itemname").value;
}).toProperty();

var submittedItems = items.sampledBy($("#purchase").asEventStream("click"));
submittedItems.onValue(function(){})

For both the version of JS there is nothing logged even if I click the buttons. According to documentation the second one should output "Executing" message on the console.

Both the code works if I attach a subscriber using onValue.

Please help me understand what's wrong?

raimohanska commented 8 years ago

There's two kinds of "laziness" involved in Bacon.js:

1) When an Observable has zero subscribers, it will not connect to its data source. As a result, nothing will happen unless you add at least one subscriber. Subscribers are added using onValue/ forEach. The log method also adds a subscriber.

2) Some methods, such as map and combine* also evaluate the event value lazily. This means that the function that you give to map as an argument won't be called unless the value is needed. This brings performance benefits in some cases such as when you combine a bunch of values from different sources using Bacon.combineWith but you need only some of the values, which you'll extract using sampledBy. In this case, the function given to combineWith will only be called for the values that are actually extracted using sampledBy.

For comparison, the first kind of laziness is implemented in other libraries too (rxjs, kefir at least), while the second kind is (afaik) only implemented in Bacon.js. I hope this helps!

raimohanska commented 8 years ago

I updated the FAQ with regard to laziness. https://github.com/baconjs/bacon.js/wiki/FAQ

web3rover commented 8 years ago

@raimohanska The above example is to demonstrate the second point. But why does it work the same way even if I use flatMap?

raimohanska commented 8 years ago

It's right there in point 1: I cannot see any subscribers in your example. If a tree falls and no-one is listening, it doesn't actually fall :)

web3rover commented 8 years ago

Added subscriber in the code. When I use flatMap and click on the purchase button after clicking on add item nothing happens. But it works in case of using map. What's the issue now?

raimohanska commented 8 years ago

Cannot see your code, so it's hard for me to tell.

web3rover commented 8 years ago

I have provided all the code above. That's is all the code.

raimohanska commented 8 years ago

Ah cool, updated code above. Lemme see..

raimohanska commented 8 years ago

Cannot say with brief look. A fiddle or similar executable demonstration case would help.

raimohanska commented 8 years ago

I removed JQuery stuff, and tried the following.

var Bacon=require("../dist/Bacon")
var addE=Bacon.Bus()
var submitE=Bacon.Bus()
var items = addE.flatMap(function(e){
      console.log("Executing", e);
      return e;
}).toProperty();

var submittedItems = items.sampledBy(submitE);
submittedItems.onValue(function(x){
  console.log("submitted", x)
})

addE.push("new item")
submitE.push("submit")

Results are as expected:

➜  bacon.js git:(master) node testing/660.js
Executing new item
submitted new item

Same results when replacing flatMap with map.

web3rover commented 8 years ago

Here is the jsFiddle https://jsfiddle.net/u01ajhsf/4/

When I use flatMap the value of the text field is never logged.

web3rover commented 8 years ago

Oops it's working now. Anyways thanks for the clarifications. Awesome library :)

raimohanska commented 8 years ago

:) No problem, mate! I'm glad you got it working. The FAQ is now also a bit improved so I guess this was a win-win.