Search code examples
javascriptangulargoogle-mapstypescriptgoogle-geocoder

Accessing data in callback function


I have a function for geocoding an address that returns the name of the city of the same address

  // geocode the given address
  geocodeAddress(address, callback) {
    this.mapsAPILoader.load().then(() => {
      var geocoder = new google.maps.Geocoder();
      geocoder.geocode({ 'address': address }, function (results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
          results[0].address_components.forEach(arrAddress => {
            if (arrAddress.types[0] == "locality") {
              callback(arrAddress.long_name);
            }
          })
        } else {
          console.log("Geocode was not successful for the following reason: " + status);
        }
      });
    });
  };

when I call that function and want to print the city name it is printed 'undefined' from the code line below the geocodeAddress function and after that is printed properly city name

this.geocodeAddress(this.offerAddress, data => {
  this.hostCity = data;
  console.log(this.hostCity);
});
console.log(this.hostCity);

I was trying to add some timeout before second console.log function but without any success

therefore, I'm interested how to access this value after returning data from geocoder because I need to use this data for storing in database and if I try to store like this

    this.geocodeAddress(this.offerAddress, data => {
            this.hostCity = data;
            this.service.addData({"address": this.offerAddress, "city": this.hostCity}, "/data")
                .subscribe(data => {
                  this.router.navigate(['list']);
                })
          });

it store data but router.navigate not working properly

so I need solution for accessing hostCity outside of geocodeAddress callback function, or how to properly call some another function inside this geocodeAddress callback function


Solution

  • If you're using TypeScript, you can make your geocodeAddress method return a Promise, instead of using callbacks, and then use async/await:

    async geocodeAddress(address): Promise<string[]> {
        return new Promise((resolve, reject) => {
            this.mapsAPILoader.load().then(() => {
               var geocoder = new google.maps.Geocoder();
               geocoder.geocode({ 'address': address }, function (results, status) {
                   if (status == google.maps.GeocoderStatus.OK) {
                       const result: string[] = [];
                       results[0].address_components.forEach(arrAddress => {
                           if (arrAddress.types[0] == "locality") {
                               result.push(arrAddress.long_name);
                           }
                       });
                       resolve(results);
                   } else {
                       console.log("Geocode was not successful for the following reason: " + status);
                       reject(status);
                   }
               });
           });
        });
    };
    

    Now, this function returns an array with the long names of all the addresses you were seeking. To use it:

    const data: string[] = await this.geocodeAddress(this.offerAddress);
    this.hostCity = data[0];
    // do whatever you want now
    

    This way you get the benefits of asynchronous programming, with the simplicity of synchronous one.