Zach’s ugly mug (his face) Zach Leatherman

Deferreds and a Better Geolocation API

November 07, 2011

Warning, this article is intended for Deferred unbelievers to convince them that Deferred objects are both easy and useful. If you’re already a Deferred object expert, you might want to skip this one.

Earlier this year I was given the opportunity to attend the jQuery Conference in San Francisco. I was delighted to go, able to finally meet some of the JavaScript greats I’d been stalking following online for years.

Looking back on the conference, the one presentation that had the biggest impact on the way that I code had to have been Dan Heberden‘s “Deferreds, Putting Laziness to Work.” (I would be remiss if I didn’t also mention inspiration from a recent presentation by Eli Perelman at the Omaha jQuery Meetup.)

At first, Deferred objects sound scary. I can assure you that they’re actually incredibly easy and incredibly useful. Today we’ll go through the simple task of reworking the Geolocation API to use jQuery Deferred objects.

Here is the standard Geolocation API to retrieve the user’s current position:

navigator.geolocation.getCurrentPosition(function(position) {
  // success
}, function(error) {
  // failure
}, {
  // options
  enableHighAccuracy: true
});

When the above API is called, a prompt is shown to the user asking if they want to divulge their location information to the domain of the currently active web site. Typically this prompt is a non-blocking asynchronous operation (although not explicitly defined in the specification).

Let’s go ahead and change it to use a jQuery Deferred object:

function getCurrentPositionDeferred(options) {
  var deferred = $.Deferred();
  navigator.geolocation.getCurrentPosition(deferred.resolve, deferred.reject, options);
  return deferred.promise();
};

Notice that the success callback is replaced by the deferred object’s resolve method and the error callback is replaced by the reject method. All of our function arguments are removed from the API. We’re left with one simple options argument.

This allows us to do things like:

getCurrentPositionDeferred({
  enableHighAccuracy: true
}).done(function() {
  // success
}).fail(function() {
  // failure
}).always(function() {
  // executes no matter what happens.
  // I've used this to hide loading messages.
});
// You can add an arbitrary number of
// callbacks using done, fail, or always.

We could also use $.when to run code upon completion of two arbitrary and contrived operations like a Geolocation call and an Ajax request. Awesome.

To coordinate between multiple Deferred objects, use $.when:

$.when(getCurrentPositionDeferred(), $.ajax("/someUrl")).done(function() {
  // both the ajax call and the geolocation call have finished successfully.
});

I wonder what other browser native APIs could be better served by using Deferred objects instead of function arguments.


< Newer
Placeholder Title for Article about HTML5 Placeholders
Older >
A Mobile Web Divided

Zach Leatherman IndieWeb Avatar for https://zachleat.com/is a builder for the web at IndieWeb Avatar for https://cloudcannon.com/CloudCannon. He is the creator and maintainer of IndieWeb Avatar for https://www.11ty.devEleventy (11ty), an award-winning open source site generator. At one point he became entirely too fixated on web fonts. He has given 79 talks in nine different countries at events like Beyond Tellerrand, Smashing Conference, Jamstack Conf, CSSConf, and The White House. Formerly part of Netlify, Filament Group, NEJS CONF, and NebraskaJS. Learn more about Zach »

3 Comments
  1. Tom Kompare Disqus

    08 Nov 2011
    This comment is only (barely) tangentially related to your post's main point (which is very helpful to me).In my experience, it may be a bit dangerous to rely on 'navigator.geolocation.getCurrentPosition' for accurate locations from devices such as mobile phones. The code is only getting the first location the hardware is able to return. Sometimes it is very accurate. Other times, the first given location is well off target, such as the nearest cell tower. I've found it may be a better user experience to use 'navigator.geolocation.watchPosition()' and let the user decide when to accept the given location. Of course this all depends on what you are trying to do.I hope this helps someone as much as reading this post about deferred helps me.
  2. Paul Irish Disqus

    09 Nov 2011
    There's a proposal for adding deferreds to ES.Next fwiw http://wiki.ecmascript.org/...
  3. Zach Leatherman Disqus

    01 Dec 2011
    @Tom: There is a "enableHighAccuracy" option, did you try that? http://dev.w3.org/geo/api/s...@Paul: Ah, cool! It would be very useful to have Deferred objects independent of jQuery.
Shamelessly plug your related post

These are webmentions via the IndieWeb and webmention.io.

Sharing on social media?

This is what will show up when you share this post on Social Media:

How did you do this? I automated my Open Graph images. (Peer behind the curtain at the test page)