0

I have a function to get a users lat long. I need to get that to return a url based on users location the url will differ. The returned url is used in a ajax call. However this second ajax call is getting hit before my first function finishes which then gives a 404 as the url is undefined.

so my code is:

$(document).ready(function () {
   getLatLong();

   if (!hasTicket) {
        doAction();
    } else {
        doAnotherAction();
        $.unblockUI();
    }
});

function doAction() {
         $.ajax({
            url: url, // this is where I am failing
            type: 'POST',
            success: function (response) {
                ticket = response.data.ticket;
                $.unblockUI();
            },
            error: function (xhr) {
                $.unblockUI();
                errorHandler("Failed" + xhr.status);
            }
        });
}

function getLatLong() {
    if (Modernizr.geolocation) {
        navigator.geolocation.getCurrentPosition(
            function (position) {
                getUrl(position.coords.latitude, position.coords.longitude);
            },
            getUrlFallback,
            { enableHighAccuracy: false, timeout: 10000, maximumAge: 360000 }
        );
    } else {
        getUrlFallback();
    }
}

function getUrl(latitude, longitude) {
    $.ajax({
        url: 'api/Controller/Action',
        type: 'GET',
        async: false, // tried making this synchronous
        data: {
            latitude: latitude,
            longitude: longitude
        },
        success: function (data) {
            url = data;
        },
        error: function (xhr) {
            errorHandler("Failed to get users nearest url: " + xhr.status);
        }
    });
}

So I call getLatLong which then calls my getUrl ajax function which has async set to false however I think it is returning from getLatLong first as my doAction function gets called and the url is then getting undefined.

How can I ensure that getLatLong and getUrl fully finish before the doAction gets run?

I tried to copy the functionality which happens after the call to getLatLong(); into a function and using $.when adn .then as below but it is still getting into the doAction method before the url is getting set.

function callAction() {
       if (!hasTicket) {
            doAction();
        } else {
            doAnotherAction();
            $.unblockUI();
        }
}

and then I had the below in doc ready:

$(document).ready(function () {

    $.when(getLatLong()).then(callAction());
});

EDIT - updated with the getUrlFallback function

function getUrlFallback () {
    // uses 3rd party geoPlugin
    getUrl(geoplugin_latitude(), geoplugin_longitude());
}
TheRiddler
  • 169
  • 3
  • 16

1 Answers1

1

You can convert your getLatLong call to return a jQuery Promise:

(NB: I'm not including the getUrlFallback logic here as you haven't included that code so I can't tell what its supposed to do):

function getLatLong() {
    return $.Deferred(function(def) {
         if (!Modernizr.geolocation) {
             def.resolve(geoplugin_latitude(), geoplugin_longitude());
         } else {
             navigator.geolocation.getCurrentPosition(
                 function(position) {
                     def.resolve(position.coords.latitude, position.coords.longitude);
                 },
                 def.resolve(geoplugin_latitude(), geopugin_longitude()),
                 { enableHighAccuracy: false, timeout: 10000, maximumAge: 360000 }
             );
         }
    }).promise();
}

And also modify getUrl such that it returns the result of $.ajax, and omitting the success and error callbacks:

function getUrl(latitude, longitude) {
    return $.ajax(...);
}

So you can now use:

getLatLong().then(getUrl).then(callAction);   // NB: no () after callAction

callAction will automatically be passed the URL, instead of it being stored in a variable in the outer scope.

Note now how the getLatLong function specifically now does that and only that. It doesn't subsequently try to convert the lat/long into a URL because getUrl does that for you (whether the URL came from the GeoAPI or the geoplugin).

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • I have updtaed with the getUrlFallback function - would you be able to Edit your answer as to how this should look now in the prmoises function – TheRiddler Jan 08 '15 at 15:01
  • and where you have showed in getUrl to return $.ajax should I remove the async :false? – TheRiddler Jan 08 '15 at 15:09
  • @TheRiddler yes - definitely remove the `async: false` - I didn't stop that in your own code. I'll take a look at the `getUrlFallback` bit – Alnitak Jan 08 '15 at 16:08
  • @TheRiddler is the `getNearestUrl` function also async? – Alnitak Jan 08 '15 at 16:10
  • no that was an old copy I copied - it should just call getUrl – TheRiddler Jan 08 '15 at 16:24
  • OK, the code above _should_ work - I just changed it so that if the Geo API isn't available, or fails, it just resolves the promise with the plugin's lat/long instead. – Alnitak Jan 08 '15 at 16:30
  • Hi - the only thing is that url variable (which is set in the success function of the getUrl function) is a global variable in the file. The whole js file is loaded on each of my page loads so getLatLong is called in doc ready to set that url global variable and then the url variable is used in other functions as well - however they are still have url as undefined currently – TheRiddler Jan 08 '15 at 18:57
  • you shouldn't do that - you need to ensure that everything else that depends on `url` isn't called except via that final callback in the `.then` chain. BTW, there isn't AFAIK any need to wait for doc ready before starting the geo-lookup. – Alnitak Jan 08 '15 at 19:09