1

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
    }
  • 2
    it's asynchronous, that's why there is a callback. You can't log outside the callback, you gotta wait for the callback to finish. – Andrea Giammarchi Jul 15 '20 at 16:52
  • Thanks for the response. How is this achieved though? Again, sorry fairly new to this. – edfrommathclass Jul 15 '20 at 16:58
  • 1
    you have at least 3 options: 1 log the address at the end of the callback within the two `if` statements, that's when you know you have an address - 2 return a promise that resolves once the address is retrieved (or rejects otherwise) - 3 return a function that will be invoked once the address is retrieved ... if you want examples, I need to write a proper answer. Do you need it? – Andrea Giammarchi Jul 15 '20 at 17:01
  • That would be very helpful. I have some sort of idea on how to implement option 2 and tried it but could not get it to work. – edfrommathclass Jul 15 '20 at 17:04

1 Answers1

2

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.

Andrea Giammarchi
  • 3,038
  • 15
  • 25