2012-12-11

Bacon.js Tutorial Part III : AJAX and Stuff


This is the next step in the Bacon.js tutorial series. I hope you've read Part II already! This time we're going to implement an "as you type" username availability check with AJAX. The steps are
  1. Create an EventStream of AJAX requests for use with jQuery.ajax()
  2. Create a stream of responses by issuing an AJAX request for each request event and capturing AJAX responses into the new stream.
  3. Define the usernameAvailable Property based on the results
  4. Some side-effects: disable the Register button if username is unavailable. Also, show a message.
I suggest you checkout the example code and switch to the tutorial-2 branch which will be the starting point for the coding part of this posting. If you just want to have a look at what we'ge got so far, have a peek.
So, at this point we've got a Property called username which represents the current value entered to the username text input field. We want to query for username availability each time this property changes. First, to get the stream of changes to the property we'll do this:
username.changes()
This will return an EventStream. The difference to the username Property itself is that there's no initial value (the empty string). Next, we'll transform this to a stream that provides jQuery compatible AJAX requests:
availabilityRequest = username.changes().map(function(user) { return { url: "/usernameavailable/" + user }})
The next step is extremely easy, using Bacon.UI.js:
availabilityResponse = availabilityRequest.ajax()
This maps the requests into AJAX responses. Looks very simple, but behind the scene it takes care of request/response ordering so that you'll only ever get the response of the latest issued request in that stream. This is where, with pure jQuery, we had to resort to keeping a counter variable for making sure we don't get the wrong result because of network delays.
So, what does the ajax() method in Bacon.UI look like? Does it do stuff with variables? Lets see.
Bacon.EventStream.prototype.ajax = function() {
  return this["switch"](function(params) { return Bacon.fromPromise($.ajax(params)) })
}
Not so complicated after all. But let's talk about flatMap now, for a while, so that you can build this kind of helpers yourself, too.

AJAX as a Promise

AJAX is asynchronous, hence the name. This is why we can't use map to convert requests into responses. Instead, each AJAX request/response should be modeled as a separate stream. And it can be done too. So, if you do
$.ajax({ url : "/usernameavailable/jack"})
you'll get a jQuery Deferred object. This object can be thought of as a Promise of a result. Using the Promise API, you can handle the asynchronous results by assigning a callback with the done(callback) function, as in
$.ajax({ url : "/usernameavailable/jack"}).done(function(result) { console.log(result)})
If you try this in you browser, you should see true printed to the console shortly. You can wrap any Promise into an EventStream using Bacon.fromPromise(promise). Hence, the following will have the same result:
Bacon.fromPromise($.ajax({ url : "/usernameavailable/jack"})).log()
This is how you make an EventStream of an AJAX request.

AJAX with flatMap

So now we have a stream of AJAX requests and the knowhow to create a new stream from a jQuery AJAX. Now we need to
  1. Create a response stream for each request in the request stream
  2. Collect the results of all the created streams into a single response stream
This is where flatMap comes in:
function toResultStream(request) {
  return Bacon.fromPromise($.ajax(request))
}
availabilityResponse = availabilityRequest.flatMap(toResultStream)
Now you'll have a new EventStream called availabilityResponse. What flatMap does is
  1. It calls your function for each value in the source stream
  2. It expects your function to return a new EventStream
  3. It collects the values of all created streams into the result stream
Like in this diagram.
flatMap
So here we go. The only issue left is that flatMap doesn't care about response ordering. It spits out the results in the same order as they arrive. So, it may (and will) happen that
  1. Request A is sent
  2. Request B is sent
  3. Result of request B comes
  4. Result of request A comes
.. and your availabilityResponse stream will end with the wrong answer, because the latest response is not for the latest request. Fortunately there's a method for fixing this exact problem: Just replace flatMap with flatMapLatest (previously called "switch") and you're done.
switch
Now that you know how it works, you may as well use the ajax() method that Bacon.UI provides:
availabilityResponse = availabilityRequest.ajax()
POW!

The easy part : side-effects

Let's show the "username not available" message. It's actually a stateful thing, so we'll convert the availabilityResponse stream into a new Property:
usernameAvailable = availabilityResponse.toProperty(true)
The boolean value is used to give the property a starting value. So now this property starts with the value true and after that reflects that value from the availabilityRequest stream. The visibility of the message element should actually equal to the negation of this property. So why not something like
usernameAvailable.not().onValue(setVisibility, unavailabilityLabel)
Once again, this is equivalent to
usernameAvailable.not().onValue(function(show) { setVisibility(unavailabilityLabel, show) })
... the idea in the former being that we partially-apply the setVisibility function: Bacon will call the function withunavailabilityLabel fixed as the first argument. The second argument to the function will be the value from theusernameAvailable.not() Property.
Finally, we'll also disable the Register button when the username is unavailable. This is done by changing
buttonEnabled = usernameEntered.and(fullnameEntered)
to
buttonEnabled = usernameEntered.and(fullnameEntered).and(usernameAvailable)
That's it for now. The result code can be found in the tutorial-3 branch.
Ideas for Part IV?

2012-11-11

Daddy, what does the "new" keyword do?


Sit down, and I'll tell you. Ok, it creates a new context object for you, then you'll have a new this to work with. This object will be the return value of the expression starting with new. For example,
> function Dog() { this.name = "poo" }
undefined
> new Dog().name
'poo'
But daddy, what happens if you accidentally forget to use new?
Um, er, lemme see.
> Dog().name
TypeError: Cannot read property 'name' of undefined
So, your constructor gets called, but nothing is returned and your Dog is undefined.
But what happened to the name?
Well, it's the name of the Window or what ever your "global object" is, depending on your execution environment. But its bed time now, good night.
< one hour passes >
Dad! I can't get no sleep! What if I return something from my Dog constructor?
< starts node.js >
Well, if you call it with new, the return value is discarded. If you forget the new, the expression evaluates to the return value.
But dad, that makes no sense and can cause unexpected application errors at runtime!!
That's right, son. That's why daddy avoids the new and this keywords. Now good night.

2012-11-08

Bacon.js Tutorial Part II: Get Started

In my previous blog posting posting, I introduced a Registration Form application case study, and then hacked it together with just jQuery, with appalling results. Now we shall try again, using Bacon.js. It'll take a while, of course, but the result will be quite pleasing, so please be patient. We'll start from the very basics.
This is how you implement an app with Bacon.js.
  1. Capture input into EventStreams and Properties
  2. Transform and compose signals into ones that describe your domain.
  3. Assign side-effects to signals
In practise, you'll probably pick a single feature and do steps 1..3 for that. Then pick the next feature and so on until you're done. Hopefully you'll do some refactoring on the way to keep your code clean.
Sometimes it may help to draw the thing on paper. For our case study, I've done that for you:
signals
In this diagram, the greenish boxes are EventStreams and the gray boxes are Properties. The top three boxes represent the raw input signals:
  • Key-up events on the two text fields
  • Clicks on the register button
In this posting we'll capture the input signals, then define the username and fullname properties. In the end, we'll be able to print the values to the console. Not much, but you gotta start somewhere.

Setup

You can just read the tutorial, or you can try things yourself too. In case you prefer the latter, here are the instructions.
First, you should get the code skeleton on your machine.
git clone git@github.com:raimohanska/bacon-devday-code.git
cd bacon-devday-code
git co -t origin/clean-slate
So now you've cloned the source code and switched to the clean-slate branch. Alternatively you may consider forking the repo first and creating a new branch if you will.
Anyway, you can now open the index.html in your browser to see the registration form. You may also open the file in your favorite editor and have a brief look. You'll find some helper variables and functions for easy access to the DOM elements.

Capturing Input from DOM Events

Bacon.js is not a jQuery plugin or dependent on jQuery in any way. However, if it finds jQuery, it adds a method to the jQuery object prototype. This method is called asEventStream, and it is used to capture events into an EventStream. It's quite easy to use.
To capture the keyup events on the username field, you can do
$("#username input").asEventStream("keyup")
And you'll get an EventStream of the jQuery keyup events. Try this in your browser Javascript console:
$("#username input").asEventStream("keyup").log()
Now the events will be logged into the console, whenever you type something to the username field (try!). To define the usernameproperty, we'll transform this stream into a stream of textfield values (strings) and then convert it into a Property:
$("#username input").asEventStream("keyup").map(function(event) { return $(event.target).val() }).toProperty("")
To see how this works in practise, just add the .log() call to the end and you'll see the results in your console.
What did we just do?
We used the map method to transform each event into the current value of the username field. The map method returns another stream that contains mapped values. Actually it's just like the map function in underscore.js, but for EventStreams and Properties.
After mapping stream values, we converted the stream into a Property by calling toProperty(""). The empty string is the initial value for the Property, that will be the current value until the first event in the stream. Again, the toProperty method returns a new Property, and doesn't change the source stream at all. In fact, all methods in Bacon.js return something, and most have no side-effects. That's what you'd expect from a functional programming library, wouldn't you?
The username property is in fact ready for use. Just name it and copy it to the source code:
username = $("#username input").asEventStream("keyup").map(function(event) { return $(event.target).val() }).toProperty("")
I intentionally omitted "var" at this point to make it easier to play with the property in the browser developer console.
Next we could define fullname similarly just by copying and pasting. Shall we?
Nope. We'll refactor to avoid duplication:
function textFieldValue(textField) {
    function value() { return textField.val() }
    return textField.asEventStream("keyup").map(value).toProperty(value())
}

username = textFieldValue($("#username input"))
fullname = textFieldValue($("#fullname input"))
Better! In fact, there's already a textFieldValue function available in Bacon.UI, and it happens to be incluced in the code already so you can just go with
username = Bacon.UI.textFieldValue($("#username input"))
fullname = Bacon.UI.textFieldValue($("#fullname input"))
So, there's a helper library out there where I've shoveled some of the things that seems to repeat in different projects. Feel free to contribute!
Anyway, if you put the code above into your source code file, reload the page in the browser and type
username.log()
to the developer console, you'll see username changes in the console log.

Mapping Properties and Adding Side-Effects

To get our app to actually do something visible besides writing to the console, we'll define a couple of new Properties, and assign our first side-effect. Which is enabling/disabling the Register button based on whether the user has entered something to both the username and fullname fields.
I'll start by defining the buttonEnabled Property:
function and(a,b) { return a && b }
buttonEnabled = usernameEntered.combine(fullnameEntered, and)
So I defined the Property by combining to props together, with the and function. The combine method works so that when eitherusernameEntered and fullnameEntered changes, the result Property will get a new value. The new value is constructed by applying the and function to the values of both props. Easy! And can be even easier:
buttonEnabled = usernameEntered.and(fullnameEntered)
This does the exact same thing as the previous one, but relies on the boolean-logic methods (andornot) included in Bacon.js.
But something's still missing. We haven't defined usernameEntered and fullnameEntered. Let's do.
function nonEmpty(x) { return x.length > 0 }
usernameEntered = username.map(nonEmpty)
fullnameEntered = fullname.map(nonEmpty)
buttonEnabled = usernameEntered.and(fullnameEntered)
So, we used the map method again. It's good to know that it's applicable to both EventStreams and Properties. And thenonEmpty function is actually already defined in the source code, so you don't actually have to redefine it.
The side-effect part is simple:
buttonEnabled.onValue(function(enabled) {
    $("#register button").attr("disabled", !enabled)
})
Try it! Now the button gets immediately disabled and will enabled once you type something to both the text fields. Mission accomplished!
But we can do better.
For example,
buttonEnabled.not().onValue($("#register button"), "attr", "disabled")
This relies on te fact that the onValue method, like many other Bacon.js methods, supports different sets of parameters. On of them is the above form, which can be translated as "call the attr method of the register button and use disabled as the first argument". The second argument for the attr method will be taken from the current property value.
You could also do the same by
buttonEnabled.assign(setEnabled, registerButton)
Now we rely on the setEnabled function that's defined in our source code, as well as registerButton. The above can be translated to "call the setEnabled function and use registerButton as the first argument".
So, with some Bacon magic, we eliminated the extra anonymous function and improved readability. Om nom nom.
And that's it for now. We'll do AJAX soon.

2012-11-04

Bacon.js Tutorial Part I : Hacking With jQuery


This is the first part of a hopefully upcoming series of postings intended as a Bacon.js tutorial. I'll be building a fully functional, however simplified, AJAX registration form for an imaginary web site.
This material is based on my presentation/hands-on session at Reaktor Dev Day 2012 where I had to squeeze a Bacon.js intro and a coding session into less than an hour. I didn't have much time to discuss the problem and jumped into the solution a bit too fast. This time I'll first try to explain the problem I'm trying to solve with Bacon. So bear with me. Or have a look at the Full Solution first if you like.
Anyway, the registration form could look something like this:
ui-sketch
This seems ridiculously simple, right? Enter username, fullname, click and you're done. As in
      registerButton.click(function(event) {
        event.preventDefault()
        var data = { username: usernameField.val(), fullname: fullnameField.val()}
        $.ajax({
          type: "post",
          url: "/register",
          data: JSON.stringify(data)
        })
      })
At first it might seem so, but if you're planning on implementing a top-notch form, you'll want to consider including
  1. Username availability checking while the user is still typing the username
  2. Showing feedback on unavailable username
  3. Showing an AJAX indicator while this check is being performed
  4. Disabling the Register button until both username and fullname have been entered
  5. Disabling the Register button in case the username is unavailable
  6. Disabling the Register button while the check is being performed
  7. Disabling the Register button immediately when pressed to prevent double-submit
  8. Showing an AJAX indicator while registration is being processed
  9. Showing feedback after registration
Some requirements, huh? Still, all of these sound quite reasonable, at least to me. I'd even say that this is quite standard stuff nowadays. You might now model the UI like this:
dependencies
Now you see that, for instance, enabling/disabling the Register button depends on quite a many different things, some of them asynchronous. But hey, fuck the shit. Let's just hack it together now, right? Some jQuery and we're done in a while.
[hack hack hack] ... k, done.
      var usernameAvailable, checkingAvailability, clicked

      usernameField.keyup(function(event) {
        showUsernameAjaxIndicator(true)
        updateButtonState()
        $.ajax({ url : "/usernameavailable/" + usernameField.val()}).done(function(available) {
          usernameAvailable = available
          setVisibility(unavailabilityLabel, !available)
          showUsernameAjaxIndicator(false)
          updateButtonState()
        })
      })

      fullnameField.keyup(updateButtonState)

      registerButton.click(function(event) {
        event.preventDefault()
        clicked = true
        setVisibility(registerAjaxIndicator, true)
        updateButtonState()
        var data = { username: usernameField.val(), fullname: fullnameField.val()}
        $.ajax({
          type: "post",
          url: "/register",
          data: JSON.stringify(data)
        }).done(function() {
          setVisibility(registerAjaxIndicator, false)
          resultSpan.text("Thanks!")
        })
      })

      updateButtonState()

      function showUsernameAjaxIndicator(show) {
        checkingAvailability = show
        setVisibility(usernameAjaxIndicator, show)
      }

      function updateButtonState() {
        setEnabled(registerButton, usernameAvailable 
                                    && nonEmpty(usernameField.val()) 
                                    && nonEmpty(fullnameField.val())
                                    && !checkingAvailability
                                    && !clicked)
      }
Beautiful? Nope, could be even uglier though. Works? Seems to. Number of variables? 3.
Unfortunately, there's still a major bug in the code: the username availability responses may return in a different order than they were requested, in which case the code may end up showing an incorrect result. Easy to fix? Well, kinda.. Just add a counter and .. Oh, it's sending tons of requests even if you just move the cursor with the arrow keys in the username field. Hmm.. One more variable and.. Still too many requests... Throttling needed... It's starting to get a bit complicated now... Oh, setTimeout, clearTimeout... DONE.
Here's the code now:
      var usernameAvailable, checkingAvailability, clicked, previousUsername, timeout
      var counter = 0

      usernameField.keyup(function(event) {
        var username = usernameField.val()
        if (username != previousUsername) {
          if (timeout) {
            clearTimeout(timeout)
          }
          previousUsername = username
          timeout = setTimeout(function() {
            showUsernameAjaxIndicator(true)
            updateButtonState()
            var id = ++counter
            $.ajax({ url : "/usernameavailable/" + username}).done(function(available) {
              if (id == counter) {
                usernameAvailable = available
                setVisibility(unavailabilityLabel, !available)
                showUsernameAjaxIndicator(false)
                updateButtonState()
              }
            })
          }, 300)
        }
      })

      fullnameField.keyup(updateButtonState)

      registerButton.click(function(event) {
        event.preventDefault()
        clicked = true
        setVisibility(registerAjaxIndicator, true)
        updateButtonState()
        var data = { username: usernameField.val(), fullname: fullnameField.val()}
        $.ajax({
          type: "post",
          url: "/register",
          data: JSON.stringify(data)
        }).done(function() {
          setVisibility(registerAjaxIndicator, false)
          resultSpan.text("Thanks!")
        })
      })

      updateButtonState()

      function showUsernameAjaxIndicator(show) {
        checkingAvailability = show
        setVisibility(usernameAjaxIndicator, show)
      }

      function updateButtonState() {
        setEnabled(registerButton, usernameAvailable 
                                    && nonEmpty(usernameField.val()) 
                                    && nonEmpty(fullnameField.val())
                                    && !checkingAvailability
                                    && !clicked)
      }
Number of variables: 6 Max. level of nesting: 5
Are your eyes burning already?
Writing this kind of code is like changing diapers. Except kids grow up and change your diapers in the end. This kind of code just grows uglier and more disgusting and harder to maintain. It's like if your kids gradually started to... Well, let's not go there.
How to improve this code? With MVC frameworks. Nope. Object-oriented design? Maybe. You'll end up with more code and better structure, but iIt will still be hard to separate concerns cleanly...
No matter what, you'll need to store the UI state, like whether or not an AJAX request is pending, somewhere. And you need to trigger things like enabling/disabling the button somewhere, and usually in many places, as in the code above. This introduces dependencies in all the wrong places. Now many different parts of code need to know about updating the status of the button, while it should be the other way around.
With the well-known Observer pattern (say, jQuery custom events) you can do some decoupling, so that you'll have an Observer that observes many events and then updates the button state. But, this does not solve the problem of providing the updateButtonState function with all the relevant data. So you'll end up using one mechanism for triggering state update and another one for maintaining required mutable state. No good.
Wouldn't it be great if you had some abstraction for a signal that you can observe and compose, so that the "button enabled" state would be a composite signal constructed from all the required input signals?
Say yes.
Good. The Property class in Bacon.js is just that: a composable signal representing the state of something. The EventStream class is a composable signal representing distinct events. Define the following signals:
var username = ..
var fullname = ..
var buttonClick = ..
The rest is just composition.
But hey, I'll get to that in the next posting.

2012-11-02

Back from Callback Hell with Bacon.js


I've heard the words Callback Hell quite a many times lately. I'll take it that you know what I'm talking about. Just gonna briefly show how to get Back from Hell with bacon.js...
So, what about if you want to perform two independent AJAX calls and do something with the values of both? You might
$.ajax("/cats").done(function(cats) {
  $.ajax("/dogs").done(function(dogs) {
    doStuffWithCatsAndDogs(cats, dogs)
  })
})
That's a minor callback hell allready. How about
var cats = Bacon.fromPromise($.ajax("/cats")
var dogs = Bacon.fromPromise($.ajax("/dogs")
Bacon.combineAsArray(cats, dogs).onValues(doStuffWithCatsAndDogs)
Know what? Your AJAX just got parallel, too. Maybe we should refactor this too:
function ajax(url) { return Bacon.fromPromise($.ajax(url)) }
Bacon.combineAsArray(ajax("/cats"), ajax("/dogs")).onValues(doStuffWithCatsAndDogs)
That's it.
Now if you wanted both cats and dogs and then do something with both and something based on an AJAX query triggered by user input... You would do
$.ajax("/cats").done(function(cats) {
  $.ajax("/dogs").done(function(dogs) {
    $("input.name").onKeyUp(function() {
      $.ajax("/stuff/" + $("input.name").val()).done(function(stuff) {
        var allTheStuff = {
          cats: cats,
          dogs: dogs,
          stuff: stuff
        }
        doStuff(allTheStuff)
      })
    })
  })
})
That's some serious callback hell. See any problems? For one, the user input won't be captured until both AJAX calls (cats, dogs) are completed, and they are not done in parallel, either. Further, the AJAX calls triggered by user input do not necessarily return in the same order as they are issued, so the last call to "doStuff" might be based on stale data. That might prove hard to fix?
With Bacon, you could do
function ajax(url) { return Bacon.fromPromise($.ajax(url)) }
function urlForStuff(name) { return "/stuff/" + name }

var name = Bacon.UI.textFieldValue($("input.name"))

Bacon.combineTemplate({
  cats: ajax("/cats"),
  dogs: ajax("/dogs",
  stuff: name.map(urlForStuff).ajax()
}).onValue(doStuff)
Now you'll start capturing user input immediately. Your initial AJAX calls are executed in parallel and combined with the latest value returned by the "/stuff/*" AJAX when all values are available.
And the ".ajax()" call actually ensures that the results are based on latest user input.