Want To Build Win8/WinJS Apps? You Need To Understand Promises.
The anonymous functions in both of these example are callback functions. They are functions that are called at some non-deterministic point in the future. They may be called immediately. They may be called a few seconds from now. They may never be called. The circumstances in which these anonymous callback functions are executed is up to the code that we are calling initially. Fortunately, these circumstances are documented within the jQuery API and documentation and we can be reasonably certain about the timing of the execution of these callbacks.
In the case of asynchronous code, such as animations, DOM click events, and AJAX calls across the network, the callback that is supplied to the method is not fired immediately. It is fired when a response from the external network call returns, when the DOM element is manipulated by the user, or when the animation completes. The function that is doing the work simply holds on to the callback reference and calls it when necessary.
The Problem With Callbacks
Look at what happens to the code when we have multiple nested callbacks:
Fortunately, it doesn’t have to be this way. There are some simple things we can do to help clean up the nested callback nightmare, including the use of delegated events within view objects (how Backbone handles DOM events in views), named methods and method pointers instead of anonymous callback methods (as described by Rob Conery in his post on cleaning up callbacks with Node – equally applicable to browser based code and WinJS code), promises (which I’ll get to in a moment) and some other interesting implementation such as using “reduce” function calls with promises, etc.
I Promise To Call It, But I Really Don’t Care About Your Callback
Promises are a powerful tool that provides some rather significant benefit to both the function that previously took a callback as a parameter, and to the code that needs to provide a callback to the method being called. In short, a promise decouples the callback from the function call, while providing a more flexible method of executing callbacks for code that is potentially asynchronous.
Ok, what that really means is that a function that previously took a callback method no longer needs to take that callback method. Instead, it can return a promise object. The promise object itself can then handle registering and calling as many callback methods as needed. When the code in the called function is done, it resolves or completes the promise (depending on which promise implementation you’re using – more on that in a bit). Resolving or completing the promise will cause all of the callbacks that are registered with the promise to be executed.
This function used to take a callback, as shown in previous examples. But now it returns a promise instead. To make use of this, we can call it like this:
Note that we are still using a callback function, but we’re no longer passing the callback in to the function that we’re executing. Instead, we’re passing it to the promise’ “then” function. This is the function, in Promises/A syntax and in (jQuery’s $.when/then syntax), that gets called when the promise is completed or resolved successfully. This means that when the function we called completes and has all of the information that it needs, it called “promise.complete()” which caused all of the registered “then” callbacks to be executed.
One of the primary benefits that promises provides to the code that returns the promise, is the decoupling of the function being called and the callbacks being executed. In the earlier examples, the function being called had to explicitly know about the callback method. It should ideally check for the existence of the callback – to make sure it’s not undefined or null – and also check to make sure it’s a function. This is a lot of noise just to call the callback, but it’s necessary noise. A promise reduces this noise significantly. If a function is creating a promise object, it doesn’t need to care about the callbacks that are going to be called. It only needs to care about calling the “complete” method (or “resolve” method on jQuery promises). Beyond this, the function returning the promise doesn’t care about your callbacks. It doesn’t need to check for valid functions, check to make sure the callback exists, or do anything else to make sure it can call the callback. It just calls “complete” and the promise worries about that other noise.
There are a number of other benefits that promises bring to the table, too: including more flexibility, the ability to manage multiple callbacks, a guarantee that your registered callback will be fired (even if it’s added after the promise is resolved), and more. While these are important topics to understand – especially the part about handling error conditions – I’m going to leave those other benefits as explanations for other material that I’ll link to at the bottom of this post.
Too Many Promises
This is rather unfortunate as it just creates a lot of confusion. There is one spec that seems to be gaining more momentum and popularity among them, though: the CommonJS Promises/A spec. The unfortunate side of this, though, is that jQuery’s promises are very different than the Promises/A spec in terms of the API and semantics of the method names. Functionally they are very much the same, but they are facilitated in ways that are just slightly different – and different enough to cause some frustration. It would be nice to see jQuery move to the Promises/A spec, but that seems unlikely at this point.
WinJS Promises: Promises/A
Hey, remember when I started this blog post and was talking about WinJS and building apps for Windows 8? … right 🙂
One of the first thing you’ll notice when getting in to WinJS development and really using the framework to build your applications, is the prevalent use of promises. And they are EVERYWHERE. The first file you open when you create a new project (other than a “blank” project) has this line of code in it, in fact:
What’s even more fun about that, though, is that this is nested inside of a callback function for an event handler. So… yeah. How’s that for some async JS love in WinJS? 🙂
For example, in the app that I’m helping to build for the P&P team, for example, we’re working on creating a “Live Tile” for the application. This involved a countless number of asynchronous calls out to the file system, to various streams of information, and to get and set the template used to create the tile for the app. We did everything we could to hide the implementation details behind some appropriate abstractions, and the code turned out clean. But it required a heavy use of promises in the calls we were making, as well as the code we wrote for our API.
Of course we could have used callbacks. It would have worked:
But I personally prefer the promises version:
It’s much more simple, much more clean, and much easier for me to read and understand.
Not A Golden Hammer Of Smashing Problems
While promises are certainly useful in cleaning up async code, and it is necessary that you understand them to work with WinJS effectively, it’s not a complete solution and you can still do stupid stuff with them. Take a look at this code, for example (part of the spike to learn how to build Live Tiles, creating a thumbnail from an existing image file):
This is a giant mess… but it uses promises! The problem here is that the promises are being abused as a fancy inline, nested callback mechanism. We may as well be using standard callbacks instead of promises at this point. Promises certainly can be used to clean up code, but promises themselves are not an end-all shiny hammer of awesomeness.
(And don’t worry about this sample too much. We’ve already cleaned this up for the real / production version of the app – this was just a spike to learn).
- CommonJS Promises wiki – hardcore, down and dirty specs for promises
- jQuery promise
- WinJS Promise object
- Asynchronous programming in Metro style apps
- WinJS Promises Sample App
I’m sure there are a thousand more articles, blog posts, videos and books on this. Feel free to drop some links in the comments with your favorite async-js / promises material.
Want To Truly Understand Promises?
If you’re looking to wrap your head around these powerful objects, check out WatchMeCode Episode 13: Promises From The Ground Up.