Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use jQuery promise/deffered in a custom function?

I have a function that gets the location through navigator.geolocation:

var getLocation = function( callback ){

    navigator.geolocation.getCurrentPosition( callback || function( position ){

        // Stuff with geolocation

    });

};

I would like to make it so that I could chain this function using jQuerys' Deffered object but I have still not managed to grasp the concept and usage of Deffered.

I'm looking for something similar to this Pseudo Code:

getLocation().then(function(){
    drawMarkerOnMap();
});

Is this syntax even possible without flipping over backwards and drowning in code?

like image 323
hitautodestruct Avatar asked Jan 17 '13 10:01

hitautodestruct


People also ask

How use JQuery Deferred and Promise?

If you are creating a Deferred, keep a reference to the Deferred so that it can be resolved or rejected at some point. Return only the Promise object via deferred. promise() so other code can register callbacks or inspect the current state. For more information, see the documentation for Deferred object.

What is Deferred function in JQuery?

Deferred() A factory function that returns a chainable utility object with methods to register multiple callbacks into callback queues, invoke callback queues, and relay the success or failure state of any synchronous or asynchronous function.

What is the difference between a Deferred and a Promise?

A promise represents a value that is not yet known. This can better be understood as a proxy for a value not necessarily known when the promise is created. A deferred represents work that is not yet finished. A deferred (which generally extends Promise) can resolve itself, while a promise might not be able to do so.


3 Answers

You have to instantiate a new deferred object and return it (or its promise) from the function. Call its .resolve method once you get the response:

var getLocation = function() {
    var deferred = new $.Deferred();

    navigator.geolocation.getCurrentPosition(function( position ){
        // Stuff with geolocation
        deferred.resolve(position);
    });

    // return promise so that outside code cannot reject/resolve the deferred
    return deferred.promise();
};

Usage:

getLocation().then(drawMarkerOnMap);

Reference: jQuery.Deferred


Addendum:

I would advise against using both approaches, deferred objects and passing callbacks to the function, to keep the interface simple. But if you have to stay backwards compatible, you can simply register the passed callback at the deferred object:

var getLocation = function(callback) {
    var deferred = new $.Deferred();

    if ($.isFunction(callback)) {
        deferred.then(callback);
    }

    navigator.geolocation.getCurrentPosition(function( position ){
        // Stuff with geolocation
        deferred.resolve(position);
    });

    // return promise so that outside code cannot reject/resolve the deferred
    return deferred.promise();
};
like image 141
Felix Kling Avatar answered Oct 16 '22 20:10

Felix Kling


Even though the above example did help me I had to do a bit more reading to wrap my head around the concept.

Below is example based on my code that contains comments to assist me when I come back to it and hopefully anyone reading this Stackoverflow question:

/* promise based getFilter to accommodate getting surrounding suburbs */
oSearchResult.fPromiseOfFilterSetting = function fPromiseOfFilterSetting(sId) {
    var self = this;
    self.oPromiseCache = self.oPromiseCache || {}; // creates a persistent cache 
                                                   // across function calls
    var oDeferred = $.Deferred(); // `new` keyword is optional
    var oPromise = oDeferred.promise();

    // leverage the cache (it's ok if promise is still pending), you can key
    if (self.oPromiseCache[sId] !== undefined) {
        return self.oPromiseCache[sId];
    }
    else {
        self.oPromiseCache[sId] = oPromise;
    }

    // do our asynchronous action below which at some point calls
    // defered.resolve(...) and hence complete our promise
    $.cmsRestProxy.doAjaxServiceRequest('ocms_searchProperties_Extension', {
        action : 'getSurroundingSuburbs',
        sSuburbIds : 'a0RO0000003BwWeMAK'
    }, function(result, json) {
        console.log("doAjaxServiceRequest(
                       'ocms_searchProperties_Extension')", json);
        oDeferred.resolve(json); // `json` is our result and `.resolve(json)` 
                                 // passes the value as first argument to 
                                 // the `oPromise.done`, `oPromise.fail` 
                                 // and `oPromise.always` callback functions
    })

    // We can now return the promise or attach optional `oPromise.done`,
    // `oPromise.fail`, and `oPromise.always` callbacks which will execute first
    // in the chain.
    //
    // Note that `oPromise.then(doneCallback, failCallback, alwaysCallback)`
    // is short form for the below
    oPromise.done(function(value) { // returned by promise.resolve(...); call
        console.log('will run if this Promise is resolved.', value);
    })
    oPromise.fail(function(value) {
        console.log("will run if this Promise is rejected.", value);
    });
    oPromise.always(function(value) {
        console.log("this will run either way.", value);
    });

    // return a promise instead of deferred object so that
    // outside code cannot reject/resolve it
    return oPromise;
}

// then to use one would do
oSearchResult.fPromiseOfFilterSetting().done(function(value) {alert(value)});

// or using $.when chaining
$.when(
    oSearchResult.fPromiseOfFilterSetting()
)
.done(
      function fDoneCallback(arg1, arg2, argN) {
          console.debug(arguments) // `arguments` is an array of all args collected
      }
);
like image 33
Daniel Sokolowski Avatar answered Oct 16 '22 19:10

Daniel Sokolowski


I know it says jQuery in the title but when I asked this question promises were new to the web and jQuery was the de facto library. Here's a more modern answer without jQuery.

Use a native Promise

All modern browsers (except for IE11 and below; use a polyfill if needed) enable you to use a native Promise construct.

let getLocation = () => {

  return new Promise( ( resolve, reject ) => {

    try {
      navigator.geolocation.getCurrentPosition( position => {
        resolve( position )
      })
    } catch ( err ) {
      reject( err )
    }

  })

};

Usage:

let runGetLocation = () => { getLocation().then( position => console.log( position ) ) }

You can also use ES2016 async/await instead of .then():

let runGetLocation = async () => {

  try {
    let position = await getLocation()
    console.log( position )
  } catch ( err ) { console.log( err ) }

}
like image 36
hitautodestruct Avatar answered Oct 16 '22 18:10

hitautodestruct