Search code examples
javascriptgoogle-api-javascript-client

Using inner function variable in outer function using Google Maps Javascript API


I am fairly new to Javascript so forgive me if this has been answered but I cannot get this to work nor can I find the answer anywhere online. I am trying to return an address using Google's Maps JavaScript API. I can print it to the console inside the inner function but if I try to access it outside of that function, it is printing undefined. How can I access the variable in the outer function and return it??

    function getAddress(latlng) {
        var address;
        var service = new google.maps.Geocoder();
        
        service.geocode({ location: latlng}, function(results, status) {
            if (status === "OK") {
                if (results[0]) {
                    address = results[0].formatted_address;
                    console.log(address); //prints address
                } 
            }
        });
        console.log(address); //prints undefined
    }

Solution

  • The issue is a classic asynchronous gotcha, where address is available only once the callback is invoked, where usually, when a callback is needed, it means there are no guarantees its result will be available outside its execution, and since I believe there is a network request involved, there's no way the result can arrive in time for you to log address right after.

    var value;
    api.thing(callbackThatSetsValue)
    console.log(value);
    

    The gits of the problem is there: value can't point at anything, as the callbackThatSetsValue will be invoked after the next line of code gets executed.

    There are at least 2 options here, let's see how these look like ...

    Wait for the result

    This options allows you to do anything you need once the address is resolved.

    function getAddress(latlng, after) {
      var service = new google.maps.Geocoder();
      service.geocode({ location: latlng}, function(results, status) {
        if (status === "OK")
          if (results[0])
            after(results[0].formatted_address);
      });
    }
    

    Above utility allows you to getAddress({lat:50,lng:1}, console.log), as example, or pass any callback that should render, use, display, such address.

    The pitfall in this approach is that if status is not OK, you have a hanging callback forever, as it won't ever get invoked.

    Alternatively, you can put an else in that function and pass an error, or null, and react accordingly in the callback.

    The Promise way

    Is the most common pattern these days, hence my suggestion.

    function getAddress(latlng) {
      return new Promise(function ($, _) {
        var service = new google.maps.Geocoder();
        service.geocode({ location: latlng}, function(results, status) {
          if (status === "OK")
            if (results[0])
              $(results[0].formatted_address);
          _(new Error("status was " + status));
        });
      });
    }
    

    In this case you getAddress({lat:50,lng:1}).then(console.log).catch(console.error), so you have an escape/way to react when address is not valid.