Most of the data in your application hasn't changed. Sure, things are being updated all the time; but, the large majority of the data remains the same from moment to moment. As such, it might make sense to show the user outdated, locally cached data before taking the time to go to the server for the true data. But how can we do this in a way that is really clear for your application Controllers? In jQuery 1.5, we were given Deferred objects to handle our asynchronous, server-side requests. In jQuery 1.7, we were given the notify() method as mean to report "partial results" to the calling context. Perhaps we can use this notify() method to report cached data as an intermediary result.
With a Deferred object, the three primary method calls are:
Resolve() finalizes a successful deferred result; reject() finalizes a failed deferred result; and, notify() announces "progress" data that has been made available before the request has been finalized. If we leave resolve() and reject() alone, and work only with the notify() binding, we can allow our application Controllers to differentiate and ignore cached data if and when they please. Not only does this allow us to enhance the application in a progressive manner, I think it also remains in alignment with the notify() intent.
To experiment with this concept, I've put together a Demo with a few small objects:
The friendGateway encapsulates the communication with the friend repository. In this case, it simply mocks server-request-latency by using setTimeout(). All calls to the friendGateway return a Deferred promise object.
The friendService encapsulates our business logic, communicating with the friendGateway and integrating the client-side data cache. As you will see below, the friendService returns its own Deferred promise objects that enhance the friendGateway promise with notify() functionality.
The controller, which makes calls to the friendService, then listens for both notify() and resolve() events, and updates the user interface as necessary:
As you can see in the Controller, the resolve() callback and the notify() callback both do the same thing - they both update the list of friends on the page. While this might seem like a duplication of effort, the intent is to provide an improved user experience. If the list of friends isn't going to change (often), we'll be able to present a more responsive interface in which the dirtiness of the data remains mostly unseen.
And, again, because the cached data is being announced via the notify() method, the Controller can completely ignore it if it wants to.
Any duplication of effort, or additional work imposed upon the browser, brings with it a certain amount of emotional discomfort. But, we have to remember that computers are hugely powerful, performing millions upon millions of operations a second. Unless rendering your interface requires a tremendous amount of effort, I'd propose that the duplication required to render intermediary, cached data will go completely unnoticed by the user. What they will notice, however, is a snappy, responsive interface.
Want to use code from this post? Check out the license.
Your article gets me close to what I am looking for, but not quite close enough. I am trying to do a series of parallel calls to a webservice and monitor progress every 1 second with a progress bar on the screen showing all progress of each parallel process.
I am also trying to get my head around the $.Deferred object.
What I need is an example that shows how to check the progress during multiple running server tasks.
In the web service, I have a session dictionary with each of the objects being processed. I want this code to get called every second to provide status to the browser, but I cannot seem to find a way to call it until the job is done. Not very interesting.
Oooh, that's a really interesting question. I know that jQuery provides a $.when() method which will take N-number of deferred values, and wraps them in new Promise object that is completed when each of the individual deferred items is completed (then each value get's passed to the new Promise callback as an individual argument).
I wonder if the $.when() can call .notify() when each encapsulated deferred is finished? Also, I wonder if there would be an easy way to determine *which* of the encapsulated deferred values is the one that just finished.
I'll put my thinking cap on.
Oh, I just realized, if you have several deferred values, then you can tap into the "done" event on each of the individual deferred values. Then you can use the $.when() to know when they are all done, and the individual deferred to know when each one is completed on its own.