Ramblings of an Independent Designer

Wednesday, October 24, 2012

Deferred objects are useful when you want to execute functions at the end of some task without having to monitor that task directly, especially when that task is being performed in the background. The code below contains a demonstration, which I’ll then start to modify to add features. It's about using Callbacks with a Long-Lived Task

The process in this example is defined by the performLongTask function, which adds together a series of numbers. I want something simple that takes a few seconds to complete, and this fits the bill.

Tip: On my system, the for loop in the performLongTask function takes about 3.5 seconds to complete, but you may need to adjust the upper limit for the loop to get a similar result on your system. Three to four seconds is a good duration for these examples. It’s long enough to demonstrate the deferred object features but not so long that you have time to make coffee while waiting for the task to complete.

Clicking the button now calls the performLongTask function. That function calls the deferred object’s resolve method when its work is complete, causing the callback function to be invoked. The performLongTask function adds its own message to the table element before it calls resolve, so you can see the sequence of progression through the script. You can see the results:

This is an example of a synchronous task. You push the button, and then you have to wait while each function that you call completes. The best indicator that you are working synchronously is the way that the Go button stays in its pressed state while the performLongTask function does its work. Definitive proof comes in the sequence of messages displayed in the image shown here. The messages from the click event handler come before and after the messages from performLongTask and the callback functions.

The major benefit of deferred objects comes when you are working with asynchronous tasks, tasks that are being performed in the background. You don’t want the user interface to lock up like it did in the previous example, so you start tasks in the background, keep an eye on them, and update the document to give the user information about the progress and result of the work.

The simplest way to start a background task is to use the setTimeout function, which means that you are using yet another callback mechanism. This may seem a little odd, but JavaScript lacks the language facilities for managing asynchronous tasks that other languages are designed with, so you have to make do with those features that are available. The code shows the example modified

I use the setTimeout function to perform the for loop in the performLongTask function after a delay of 10 milliseconds. You can see the effect this has in the image below. Notice that the messages from the click handler function appear before those from the performLongTask and callback functions. If you run this example yourself, you will notice that the button pops back into its regular state immediately, rather than waiting for the work to complete.

Callbacks are particular important when working with background tasks because you don’t know when they are complete. You could set up your own signaling system—updating a variable, for example—but you would need to do this for every background task that you perform, which becomes tiresome and error-prone very quickly. Deferred objects allow you to use a standardized mechanism for indicating that tasks have completed, and as I’ll demonstrate in later examples, they offer a lot of
flexibility in how this is done.

Can we do better?

Before you start digging into the feature of deferred objects, I am going to update the example to use the pattern that I tend to work with in real projects. This is purely personal preference, but I like to split out the workload from the asynchronous wrapper and integrate the production of the deferred object into
the function. The code shows the changes.

I have separated out the code to perform the task asynchronously. This is in the performLongTask function, which is an asynchronous wrapper around the performLongTasksync function and which uses a deferred object to trigger callbacks when the work has completed. Here is the revised performLongTask function:

If pass a function to the Deferred method, it is executed as soon as the object is created, and the function is passed the new deferred object as a parameter. Using this feature, I can create a simple wrapper function that performs the work asynchronously and triggers the callbacks when the work has finished.

If you are observant, you will have noticed that there is a chance that calling the done method to register a callback function may occur after the task has been completed and the resolve method has been called. This may occur for very short tasks, but the callback function will still be called, even if done is called after resolve.

The other reason that I like to create a wrapper like this is because deferred objects can’t be reset once they are resolved or rejected (I explain rejection in a moment). By creating the deferred object inside of the wrapper function, I ensure that I am always using fresh, unresolved deferred objects. The other change I have made to this example is to add a toggle button that allows the task to be performed synchronously or asynchronously. I will take this feature out of future examples because this is an article about asynchronous tasks, but it is a good way to make sure you are comfortable with the difference. You can see the output from both modes

Blog Archive

About Me

I am a professional frontend web developer and entrepreneur who is fascinated with the web industry, business, technology, and how they fit together. I am also the founder of Walsh Design Labs, a design studio in LA. When I am not building software, I love spending time with my beautiful wife, Catherine, and my spunky little dog, Buxo. I do not have a Twitter or Facebook account, as I feel it takes away my family time.