简体   繁体   中英

Navigator - getCurrentPosition() promise resolves before location is found

This code is for a 'zillow' clone as a portfolio project.

My issue: I made a promise that retrieves the user's location using the geolocation API. I need this code to run asynchronously however, when the code executes, it resolves the promise before the user's location is found. The code works when I wrap certain parts of it in a timeout function but I really want to figure this out with using only promises. How do I fix this issue? Help is super appreciated!

PS: I have not programmed my error handling yet, I wanted to get the above issue out of the way before doing so. Also, if you need any other files just let me know and I will upload them however, I think the issue is in either of the two files that are displayed below

Below is code from my 'controller.js' file:

const controlSearchCurrentLocation = async function () {

  await findLocationView.getUserLocation(model.storeCurrentLocation);

  /*
   The setTimeout function is only used in order to give the 
   'findLocationView.getUserLocation(model.storeCurrentLocation)' time 
   to find and store the user's location
  */

  setTimeout(() => {
    currentCityResultsView.displayResults(
      model.sendCurrentCityState,
      model.storeData,
      model.sendCoords,
      model.sendHomesCurrentLocation,
      model.sendHomesLength
    );
  }, 4000);
};

const init = function () {
  controlSearchCurrentLocation();

  controlSearch();
};

init();

Below is from my 'findLocationView.js' file:

class FindLocationView extends AppView {

  getUserLocation(storeCurrentLocationFunc) {
    return new Promise((resolve, reject) => {
      navigator.geolocation.getCurrentPosition(
        function (position) {
          this._success(position, storeCurrentLocationFunc);
        }.bind(this),
        this._failure,
        this._options
      );

      if (navigator.geolocation) resolve("stored user location");
      if (!navigator.geolocation) reject("could not store user location");
    });
  }

  async _success(position, storeCurrentLocationFunc) {
    let city, state;
    const { latitude, longitude } = position.coords;
    [...this._coordinates] = [latitude, longitude];
    [this._latitude, this._longitude] = [latitude, longitude];

    mapView.initMap(this._coordinates);

    const data = await helpers.ajaxLocation(this._latitude, this._longitude);

    city = data.results[0].address_components[2].long_name;
    state = data.results[0].address_components[4].short_name;
    storeCurrentLocationFunc(this._coordinates, city, state);
  }

  _failure(error) {
    console.warn(`ERROR(${error.code}): ${error.message}`);
  }

  _options = {
    enableHighAccuracy: true,
  };
}

export default new FindLocationView();

Ok, so I seem to have found a solution. If I can do anything to make this better, or if there is another solution please let me know. Thanks!

Controller: Mostly the same however, I no longer need the setTimeout function since the controlSearchCurrentLocation function seems to be executing code asynchronously.. (see below)

const controlSearchCurrentLocation = async function () {

  await findLocationView.getUserLocation(model.storeCurrentLocation);

  currentCityResultsView.displayResults(
    model.sendCurrentCityState,
    model.storeData,
    model.sendCoords,
    model.sendHomesCurrentLocation,
    model.sendHomesLength
  );
};

const init = function () {
  controlSearchCurrentLocation();

  controlSearch();
};



init();

My biggest correction was here in my findLocationView class. Rather than resolving the promise outside of the getCurrentPosition function, I passed it into the function and I have it executing the '_success' function upon resolving. This makes it impossible for the promise to resolve before the user's location is found.

class FindLocationView extends AppView {

  getUserLocation(storeCurrentLocationFunc) {
    return new Promise((resolve, reject) => {
      navigator.geolocation.getCurrentPosition(
        function (position) {
          resolve(this._success(position, storeCurrentLocationFunc));
        }.bind(this),
        this._failure,
        this._options
      );

      if (!navigator.geolocation) reject("could not store user location");
    });
  }

  async _success(position, storeCurrentLocationFunc) {
    let city, state;
    const { latitude, longitude } = position.coords;
    [...this._coordinates] = [latitude, longitude];
    [this._latitude, this._longitude] = [latitude, longitude];

    mapView.initMap(this._coordinates);

    const data = await helpers.ajaxLocation(this._latitude, this._longitude);

    city = data.results[0].address_components[2].long_name;
    state = data.results[0].address_components[4].short_name;
    storeCurrentLocationFunc(this._coordinates, city, state);
  }

  _failure(error) {
    console.warn(`ERROR(${error.code}): ${error.message}`);
  }

  _options = {
    enableHighAccuracy: true,
  };
}

export default new FindLocationView();

Although you managed to solve. The reason you ended up with the problem is from mixing concerns.

  const userLocation = await findLocationView.getUserLocation();

Ideally you want getUserLocation to resolve the position, nothing else. It doesn't store the location.

navigator.geolocation.getCurrentPosition(resolve, reject);

Now you have a separation of concern

// getUserLocation no longer takes a param
const userLocation = await findLocationView.getUserLocation();
// Wait until userLocation is stored
await _success(userLocation, model.storeCurrentLocation);
// We can now show results
currentCityResultsView.displayResults();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM