Deferred Objects

Deferred Objects

Sandro Padin

August 09, 2013

A Deferred object is a mechanism for referencing a value which may (or may not) be available at a later point.

Every so often you might require a group of operations to complete before you continue. These operations might need to happen either in sequence or in any order, but they all have to finish before the code can continue.

Using deferred objects for Ajax calls

A popular example of a deferred object is making multiple concurrent ajax calls and then using the data returned after all requests have completed.

ajax1 = $.get("/file1.json")
ajax2 = $.get("/file2.json")

$.when(ajax1, ajax2).then (response1, response2) ->
		# ready to continue with data from both sources


The code examples in this article are written in CoffeeScript which compiles down to JavaScript.

The intent of this code reads clearly. When the two ajax calls have finished loading, execute the anonymous function.

The $.when and deferred.then functions are part of the jQuery Deferred API. Also of note, $.get returns a jqXHR object, which implements the promise interface. That's the reason we can use it as a parameter in the $.when function.

In the example above, the two ajax calls can occur independently. Sometimes, however, you might need the response from the first ajax call before you can continue with the second operation. Normally, you could create a callback chain like the one below:

$.get
		url: "/json/source-1"
		success: (response1) ->
				$.get
						url: "/json/source-2"
						data: { someVar: response1.someVal }
						success: (response2) ->
								$.get
										url: "/json/source-3"
										data: { someVar: response2.someVal }
										success: (response3) ->
												// finally do something here


A more elegant solution is to use a when/then chain which makes the code appear less cluttered:

ajax1 = $.get({
		url: "/json/source-1"
})

ajax2 = (response1) => {
		return $.get({
				url: "/json/source-2",
				data: { someVar: response1.someVal }
		})
}

ajax3 = (response2) => {
		return $.get({
				url: "/json/source-3",
				data: { someVar: response2.someVal }
		})
}

chain = $.when(ajax1).then(ajax2).then(ajax3)
chain.done((response3) => {
		// finally do something here
})


In the example above ajax1 is a jqXHR object whereas ajax2 and ajax3 are functions that return a jqXHR object. The distinction is important. The deferred.then function expects a callback which can return a promise. If any of the ajax requests fail, the chain.done callback will never be triggered, just as you'd expect.

Usefulness aside from Ajax requests

Ajax requests are the best examples of Deferred Objects because they're so commonly used. But some other cases for Deferred Objects include file access, web workers or other asynchronous calls.

An interesting use I've toyed with is user interaction. Embedded below is an example that shows how to combine deferred objects with mouse events to create a simple mouse dexterity game.

Code examples

Play the game in a new window or view the source on Github.

In the game above, all of the squares have a deferred object attached to their mouse over event. The player wins if they can trigger all of the red squares before mousing over any grey squares.

Take a look at the source and specs for this game on Github if you want to learn more.

How does this work?

I use Deferred objects in two parts.

# source:
# src/javascripts/game.js.coffee:30

$.when(safePiecesMousedOver...).then =>
		@handleGameover true

_.each safePiecesMousedOver, (piece) =>
		pieceMousedOver = piece.promise()
		$.when(pieceMousedOver).then (evt) =>
				target = evt.currentTarget
				$(target).css("opacity", "1")
				unless @running or @gameover
						$("img").hide()
						@running = true
						@startTimer()

_.each nonSafePiecesMousedOver, (piece) =>
		pieceMousedOver = piece.promise()
		$.when(pieceMousedOver).then =>
				@handleGameover false
				@reset()


view source on Github

In this set of calls I attach callbacks for three cases: All safe (red) pieces being moused over, any red piece moused over, and any non-safe (grey) piece moused over. I do various actions based on any of those three events.

Another section where I use Deferred objects is in the Timer class. It is a small class so I've included it below.


# source:
# src/javascripts/timer.js.coffee

class window.Timer
		constructor: ->
				@reset()

		tick: =>
				currentTime = +(new Date())
				@elapsedTime = currentTime - @startTime
				@deferred.notify(@elapsedTime)

		start: ->
				unless @timer
						@elapsedTime = 0
						@startTime = +(new Date())
						@timer = setInterval(@tick, 10)

		stop: ->
				@deferred.resolve()
				clearTimeout(@timer)
				@timer = null

		reset: ->
				@deferred = jQuery.Deferred()
				@deferred.progress(@cb)

		progress: (@cb) ->
				@deferred.progress(@cb)


view source on Github

When you create a new Timer, you can attach a listener for its progress. This listener will then invoke your callback every 10 milliseconds with the elapsed time. This is how the timer on the game works. My progress callback updates the view with your current time.

Resources

If you'd like to find more information about Deferred objects or other forms of asynchronous control flows, check out the articles below.