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?
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();
};
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
}
);
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 ) }
}