Search code examples
javascriptajaxgeolocation

How to get a value from an async operations in JS?


I've read some forums and watched some youtube videos about asyn-operations and using promises to resolve these kind of problems, but they were only working on easier functions and such. that it doesn't actually give me a hint on how to solve my problem. I have this function inside my script.

var coordinates = [];
    $(function(){
      function getcoordinates(){
          if(navigator.geolocation){
            var options = {
              enableHighAccuracy: true,
              timeout: 5000,
              maximumAge: 0
            };
             function success(pos) {
                var crd = pos.coords;
                userlat = crd.latitude;//this one
                userlong = crd.longitude;// and this one are the ones i want
              }
              function error(err) {
                console.warn(`ERROR(${err.code}): ${err.message}`);
              }
            navigator.geolocation.getCurrentPosition(success, error, options);//but I dont have an idea how to access those two from this API function.
          }
          else
            alert("Geolocation is not supported by this browser.");
        }

      $('#go').click(function(){
        console.log("outside success output: " + userlat);//this still shows 0
        console.log(coordinates);

        $.ajax({
          url:"dbphp.php",
          method:"POST",
          data:{userlong:coordinates[1], userlat:coordinates[2]},
          success:function(data){
          }
        })

      })
    })

how do I return the values of userlat and userlong and put it into my coordinates array?


Solution

  • First of all let's agree on a definition of what you call "async": a non-blocking piece of code that will be executed later on when a condition has been fulfilled right ?

    By this definition there are some "async" calls in your code alright, navigator.geolocation.getCurrentPosition(), $.ajax(), but also .click() and $().

    Each of which will call back a provided function that you provide (callback).

    In case of getCurrentPosition(), it receives 3 parameters, 2 of which are callbacks respectively executed upon success / failure (optional), and a configuration object (optional). And you are definitely handling that alright.

    But:

    1. your syntax seem a bit wrong, getcoordinates() is not triggered by anything previous to the button click
    2. either way you are not specifically waiting for getcoordinates() to finish before handling the returned value in your .click() handler
    3. you shouldn't scope unecessarily your function definitions
    4. where are userlat and userlong declarations ?
    5. don't forget that arrays are 0 indexed

    TL;DR play around with this:

    function getCoordinates(thenCb, errorCb) {
        thenCb = thenCb || function () {};
        errorCb = errorCb || function () {};
    
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(thenCb, errorCb, {
                enableHighAccuracy: true,
                timeout: 5000,
                maximumAge: 0
            });
        } else {
            alert('Geolocation is not supported by this browser.');
        }
    }
    
    $(function () {
        $('#go').click(function () {
            getCoordinates(function (pos) {
                $.ajax({
                    url: 'dbphp.php',
                    method: 'POST',
                    data: { 
                        userlong: pos.coords.latitude, 
                        userlat: pos.coords.longitude 
                    },
                    success: function (data) {
                        // do stuff with data...
                    }
                });
            }, function (err) {
                console.warn('Geolocation error ' + err.code + ': ' + err.message);
            });
        });
    });
    

    For an ES6+ version:

    const getCoordinates = (thenCb = () => {}, errorCb = () => {}) => {
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(thenCb, errorCb, {
                enableHighAccuracy: true,
                timeout: 5000,
                maximumAge: 0
            });
        } else {
            alert('Geolocation is not supported by this browser.');
        }
    };
    
    $(() => {
        $('#go').click(() => {
            getCoordinates(({ coords }) => {
                const { userlong, userlat } = coords;
                $.ajax({
                    url: 'dbphp.php',
                    method: 'POST',
                    data: { userlong, userlat },
                    success: (data) => {
                        // do stuff with data...
                    }
                });
            }, (err) => {
                console.warn(`Geolocation error ${err.code}: ${err.message}`);
            });
        });
    });
    

    Note 1: You might want to refactor this based on your needs and context, avoid setting getCoordinates() in global scope, etc.

    Note 2: It is okay in this case to wait for getCoordinates() completion on user click because, although "async", the function does return results swiftly. Usually when it takes longer to resolve you might want to a) fire the function previous to the click handler in order to memoize its return value, b) show a loading state to the user.

    Regarding the xhr request, note that you are using jQuery.ajax() and the response data will be available in your success callback provided as its first argument.

    Also, be sure to check out the docs:
    - https://developer.mozilla.org/en-US/docs/Web/API/Geolocation
    - https://api.jquery.com

    If interested, read on how to make use of Promises, to make callback code a lot more digest: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

    If still interested, read on how to use async / await to make Promises code a lot more digest: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function