0

I need to interchangeably get the user's geolocation and output the latitude/longitude coordinates into text fields. These must be in unique functions, as they will be used by different functionality interchangeably. Most importantly, the functions should run synchronously because the values used by the second function are made available by the first function.

Being new to both geolocation and async functions I'm not entirely sure which part below is broken, as the code below is a combination of many different tutorials used.

The requirement is to:

  1. Get the user's geo-coordinates (on-demand)
  2. Save these to a page-level variable
  3. Output the geo-coordinates into a text field (on-demand)

let latField = document.getElementById("EventSearch_Latitude");
let longField = document.getElementById("EventSearch_Longitude");
let myLocation = null;

// Get the user's geo-coordinates
async function GetMyLocation() {

  console.log("Determining position");

  myLocation = null;

  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(
      (position) => {
        myLocation = position;
        console.log("Position determined");
      },
      () => {
        console.log("Geolocation is not supported by this browser, or was denied.");
      }
    );
  } else {
    console.log("Geolocation is not supported by this browser, or was denied.");
  }

  return Promise.resolve(1);
}

// Put the geo-coordinates into text fields
async function OutputMyLocation() {

  console.log("OutputMyLocation()");

  latField.value = "";
  longField.value = "";

  if (myLocation != undefined && myLocation != null) {
    latField.value = myLocation.coords.latitude;
    longField.value = myLocation.coords.longitude;
  }

  console.log("End OutputMyLocation()");
  return Promise.resolve(1);
}

// Runs on page load

GetMyLocation()
  .then(() => {
    console.log(myLocation);
    OutputMyLocation();
  });
<p>
  Latitude
  <input type="text" id="EventSearch_Latitude" name="EventSearch.Latitude" value="">
</p>

<p>
  Longitude
  <input type="text" id="EventSearch_Longitude" name="EventSearch.Longitude" value="">
</p>

I'm not sure if StackOverflow allows navigator.geolocation.getCurrentPosition, but in my local console I get this output:

Determining position

null

OutputMyLocation()

End OutputMyLocation()

Position determined

It appears (to me) that OutputMyLocation() is not waiting for GetMyLocation() to finish, so the script is trying to output the geo-coordinates before they've been determined. From the tutorials I read I had assumed that using Promise.resolve and then would overcome this.

What is my mistake please?

EvilDr
  • 8,943
  • 14
  • 73
  • 133
  • It's information overload which is what I'm suffering already. That answer has three different approaches plus JQuery. I'm trying to learn by doing, so just a clear example using `then` and `return promise` would be nice, but it's a minefield for the uninitiated... – EvilDr Dec 29 '22 at 09:53
  • 1
    You cannot use the geo API synchronously. Period. End of story. It’s an asynchronous API and needs to be used as such. You should make your “interchangeable API” all asynchronous, if one necessary part of it is asynchronous. The best you could do synchronously is return `null`, try to get the position in the background, save it to a variable, and return it directly the *next* time you call your function. But that’s arguably not very useful. – deceze Dec 29 '22 at 10:22
  • @deceze that's useful to know thank you. In the larger scope, the idea is that the browser requests the user's location and if returned, renders a Google Map with that position highlighted. Obviously there's lots going on asynchronously so the challenge was how to wait for the location to be returned and THEN position it on the map... – EvilDr Dec 29 '22 at 10:25

1 Answers1

1
  • Don't use async if you don't await in the function's body.
  • return a new Promise from GetMyLocation. Don't juse resolve(1) makes no sense, resolve with the returned data instead

Example:

const elLat = document.querySelector("#EventSearch_Latitude");
const elLng = document.querySelector("#EventSearch_Longitude");

// Get the user's geo-coordinates
const getMyLocation = () => new Promise((resolve, reject) => {
  if (!navigator.geolocation) return reject("Geolocation is not supported by this browser, or access was denied.");
  navigator.geolocation.getCurrentPosition(resolve, reject);
});

// Put the geo-coordinates into text fields
const outputMyLocation = (myLocation) => {
  elLat.value = myLocation?.coords?.latitude ?? "";
  elLng.value = myLocation?.coords?.longitude ?? "";
};

// Runs on page load
getMyLocation().then(outputMyLocation);
<p>Latitude <input type="text" id="EventSearch_Latitude" value=""></p>
<p>Longitude <input type="text" id="EventSearch_Longitude" value=""></p>

your second function only needs to be asynchronous if you plan to use it in an async/await way (without the .then()) like:

const elLat = document.querySelector("#EventSearch_Latitude");
const elLng = document.querySelector("#EventSearch_Longitude");

// Get the user's geo-coordinates
const getMyLocation = () => new Promise((resolve, reject) => {
  if (!navigator.geolocation) return reject("Geolocation is not supported by this browser, or access was denied.");
  navigator.geolocation.getCurrentPosition(resolve, reject);
});

// Put the geo-coordinates into text fields
const outputMyLocation = async (myLocation) => {

  let myLocation;

  try {
    myLocation = await getMyLocation();
  } catch (err) {
    console.error(err);
  }

  elLat.value = myLocation?.coords?.latitude ?? "";
  elLng.value = myLocation?.coords?.longitude ?? "";

};


outputMyLocation();
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • That's very helpful. Thank you for using my logic to base your answer on. That is very helpful to see how it fits together along with your advice. – EvilDr Dec 29 '22 at 10:23