0

I have a function that uses the geocoding api to turn a string(address/zip/city) into coordinates. I have another function that needs the coordinates returned from the geocoding api to call a different api that searches for events. However, I am new to async javascript and I am having trouble with async/await. The event api is saying that the coordinates that should return from the geocode api are undefined, but a few seconds later, the coordinates are returned. I need the event api to stop execution until the coordinates are returned from the geocoding api. Can somebody please point me in the right direction?

Here is the code for the two functions

function toLatLng(input) {
  $.ajax({
    type: "get",
    url: GEOCODE_API_URL + input,
    datatype: "json",
    success: (data) => {
      if(data.status === "OK") {
        return new Promise((resolve, reject) => {
          resolve(data.results[0].geometry.location);
        });
      }
      else if(data.status === "ZERO_RESULTS") {
        alert("The search yielded no results");
      }
      else {
        alert("Unable to search for events");
      }
    },
    error: (error) => {
      console.log(error);
    },
  });

async function getEvents(input) {
   let coordinates = await toLatLng(input);
   let queryStr = `&latlong=${coordinates.lat},${coordinates.lng}`
   $.ajax({
     type: "get",
     url: API_URL + queryStr,
     datatype: "json",
     success: (data) => {
       console.log(data);
     },
     error: (err) => {
       console.log(error);
     },
   });
 }
mobes
  • 25
  • 4
  • @JaromandaX jQuery promises are Promise/A compliant since forever, so it's not an "almost promise". – Tomalak Sep 26 '20 at 07:12
  • @JaromandaX jQuery 3 has been around since 2015. (compare https://stackoverflow.com/questions/23951745/is-any-jquery-version-compliant-to-promise-a-specifications). So yes, it's fully Promise/A compliant and yes, it's been since forever. Update your old knowledge. ;) – Tomalak Sep 26 '20 at 07:24
  • @JaromandaX Yeah, I know... (I wouldn't have objected if you had said *"old versions of jQuery...*") – Tomalak Sep 26 '20 at 07:40
  • @Tomalak - unfortunately, 2.x is the most used series :p - anyway, I'm going to remove these comments now :p - as it adds nothing to the question – Jaromanda X Sep 26 '20 at 07:43
  • @JaromandaX you'll have to back that up with some stats. I didn't think *anyone* used jquery v2 - most people are still on 1.x - see https://w3techs.com/technologies/details/js-jquery – freedomn-m Sep 26 '20 at 07:54
  • ouch!!! 1.x @freedomn-m - that's even worse than I thought – Jaromanda X Sep 26 '20 at 07:55

2 Answers2

1

You can use 2 different approaches.

  1. Return promise from toLatLng method like:

function toLatLng(input) {
  return new Promise((resolve, reject) => {
    $.ajax({
      type: "get",
      url: GEOCODE_API_URL + input,
      datatype: "json",
      success: (data) => {
        if (data.status === "OK") {
          resolve(data.results[0].geometry.location);
        } else if (data.status === "ZERO_RESULTS") {
          reject("The search yielded no results");
        } else {
          reject("Unable to search for events");
        }
      },
      error: (error) => {
        reject(error);
      },
    });
  });
}

async function getEvents(input) {
  toLatLng(input).then(coordinates => {
    let queryStr = `&latlong=${coordinates.lat},${coordinates.lng}`
    $.ajax({
      type: "get",
      url: API_URL + queryStr,
      datatype: "json",
      success: (data) => {
        console.log(data);
      },
      error: (err) => {
        console.log(error);
      },
    });
  }).catch(err => {
    console.log(err);
  });
}
  1. Pass callback functions to toLatLng to call upon success or failure.

function toLatLng(input, successCallback, failureCallback) {
  $.ajax({
    type: "get",
    url: GEOCODE_API_URL + input,
    datatype: "json",
    success: (data) => {
      if (data.status === "OK") {
        successCallback(data.results[0].geometry.location);
      } else if (data.status === "ZERO_RESULTS") {
        failureCallback("The search yielded no results");
      } else {
        failureCallback("Unable to search for events");
      }
    },
    error: (error) => {
      failureCallback(error);
    },
  });
}

function onCoardinateLoaded(coordinates) {
  let queryStr = `&latlong=${coordinates.lat},${coordinates.lng}`
  $.ajax({
    type: "get",
    url: API_URL + queryStr,
    datatype: "json",
    success: (data) => {
      console.log(data);
    },
    error: (err) => {
      console.log(error);
    },
  });
}

function getEvents(input) {
  toLatLng(input, onCoardinateLoaded, onFailure);
}

function onFailure(message) {
  console.log(message);
}
Dipen Shah
  • 25,562
  • 1
  • 32
  • 58
  • At the first option in the `toLatLng` function, wouldn't it be better if instead of `alert` the promise will reject? – Saar Davidson Sep 26 '20 at 07:18
  • 1
    @SaarDavidson it would be definitely, I didn't fix all issues in the OPs call just promisified it. Updated to have reject! – Dipen Shah Sep 26 '20 at 07:19
  • 1
    Thank you so much for the help. I used the promise based approach and it works perfectly. I will use reject instead of alert in the toLatLng function as well. – mobes Sep 26 '20 at 07:30
0

There are a couple of mistakes in your code.

  • Most importantly, a function that you want to be able to await needs to either return a promise, or be async itself (technically that's the same thing). Your geocode() function is neither async nor does it return anything, so it can't be awaited.
  • Next, your geocode() function return value is way too specific. Don't make it return the "first result". Make it return all results. The calling code can decide whether it only wants to use the first one. This way your geocode() function becomes much more versatile.
  • Next, your functions seem to be pasing raw query strings around to build URLs manually. Don't do that. Pass objects (key/value). jQuery will turn them into query strings when appropriate.
  • If you use async/await, you should use try/catch at some point.
  • Next, your functions can be condensed quite a bit.

Improved approach (jQuery version should be 3.0 or higher for this):

async function geocode(address) {
  var response = await $.get(GEOCODE_API_URL, {
    address: address,
    any: "other",
    parameters: "here",
    key: "YOUR_API_KEY"
  });
  if (response.status === "OK") return response.results;
  if (response.status !== "ZERO_RESULTS") console.log("geocode API status unexpected", response.status, response);
  return [];
}

async function getEvents(input) {
  var results = await geocode(input);
  if (!results.length) return;

  var coordinates = results[0].geometry.location;
  try {
    let data = await $.get(API_URL, {
      latlong: `${coordinates.lat},${coordinates.lng}`
    });
    console.log("getEvents SUCCESS", data);
  } catch (err) {
    console.log("getEvents ERROR", err);
  }
}
 
Tomalak
  • 332,285
  • 67
  • 532
  • 628