TIL: Knockout View Models with jQuery Deferreds

Do It Later. Meaning. Defer it. Get it?I’m a knockout.js beginner and I’m no JavaScript Ninja. I’ve got a lot of habits that are carryovers from my years of C# and I sometimes find myself trying too hard to make those habits work in JavaScript when there are better ways. I learned how to use jQuery deferred objects and promises inside of a knockout view model.

I was working on a project that relies heavily on knockout.js for data-binding and its MVVM awesomeness. Inside my view model, I had a method that made a REST API call and I wanted to do something unrelated to the view model when that call finished. Simple right? Just use a callback! That’s what I thought.

What is a jQuery Deferred?

You can read more about jQuery deferreds on their site and more about promises at CommonJS Promises/A design. It boils down to receiving notification when a synchronous or asynchronous function has completed (either successfully or not) and executing arbitrary code in turn.

Why Deferreds?

Why not just use a callback parameter? Sure, that would work. In fact, my first instinct was to pass in a callback function as a parameter to the method. I do this in C#, so I wrote the code and it ran as expected. The main issue is that it’s messy and had nothing to do with the view model. It’s only there for program flow. My view model felt dirty. Someone pass the granola.
Using deferreds allows me to run code after the asynchronous call completes.
Sure, jQuery has callbacks all over the place and all those callbacks are parameters to some function somewhere in the framework. If it’s good enough for them, who am I to question? True, but deferreds were introduced in jQuery 1.5. They made it better. We can use the callback method, but deferreds give us a better way (depending on the circumstances).

Any other options?

There were two other options I tried: parent references and events. For the parent reference, I had passed the view model a reference to the parent that created it. This allowed me to call methods on the parent from within the child. It’s kind of like watching drunk ugly people make out at a wedding reception. Nasty. That’s why I went looking for something else.

I also tried subscribing to and firing custom events. This is common in .NET so I’m used to programming this way. I used the jQuery methods .bind() and .triggerHandler() to accomplish this. It’s not bad. But it seemed like I was trying too hard. There was just a lot of housekeeping that I didn’t want to do.

And then there’s JavaScript promises. More specifically, deferreds in jQuery. There’s some documentation around the web about using jQuery deferreds and promises, but I had yet to see one that specifically used Knockout view models. This is why we’re here.

This is an example of what I started with: You can see this in action over on JSFiddle

See that I’m directly calling the view model’s method and inside that AJAX call, I do work inside the success callback. In this trivial case, it’s okay, but what I was trying to do required the caller to do work after the call returned that had nothing to do with the view model. Unfortunately, because the AJAX call is asynchronous, the method call was returning right away even though the AJAX call hadn’t finished yet. I had a race condition that wouldn’t necessarily show up during debugging.

Using deferreds allows me to run code after the asynchronous call completes. In the code above, note that this method calls the function $.ajax() which is the jQuery way of doing an AJAX op. This is lucky for me because the $.ajax() already implements promises so there’s only a single change I have to make:

[code language=“javascript”] return [/code]

I need to change the method to return the AJAX call. By doing this, we implicitly return a promise to the caller. This, therefore, allows us to use the $.when() and $.then() calls.

Here’s the JSFiddle for the updated version.

That’s it! I can now chain together lots of code to run after the method actually completes without having to muck up my method with callback parameters, parent references, or event handlers.

Final Note….My Gotcha

The thing that confused me for much too long is that the code in the $.then() must be a function declaration. If it’s not but is, say, just a code snippet, that snippet will be executed immediately without so much as a courtesy wait for the promise to complete. It will execute as soon as the call to the promise is done which we know is not the same as waiting for the asynchronous operation of the promise to actually complete. Confused? Just look at the code samples. Or ask a question in the comments.