0

The getLocation() function that should retrieve location in 3 attempts returns undefined instead. navigator.geolocation.getCurrentPosition() returns the correct position, but the problem is in the promise handling.

The problem is apparently that I am calling a promise inside promise. I am not allowed to use the await keyword inside geolocate() already declared as async.

Original call:

var getLocationPromise = this.getLocation();
// Do something...
location = await getLocationPromise;

getLocation():

  async getLocation() {
    return new Promise((resolve, reject) => {
      var geolocate;
      for (let i=0; i<3; i++) {

        geolocate = this.geolocate();

        try {
            var location = geolocate;//CAN'T USE AWAIT INSIDE ASYNC...
            resolve(location);
        } catch(err) {
            continue;
        }
      } 
      reject("Max geolocation attempts");
    });
  }

geolocate():

  async geolocate() {
    return new Promise((resolve, reject) => {

      navigator.geolocation.getCurrentPosition(
        (position) => {
          resolve(position);
        },
        (err) => {
          reject(err);
        },
        {enableHighAccuracy: true, timeout: 20000, maximumAge: 1000}
      );
    });
  }
Peter G.
  • 7,816
  • 20
  • 80
  • 154

2 Answers2

2

As long as the following is in a function declared async

var getLocationPromise = this.getLocation();
// Do something...
location = await getLocationPromise;

it should be fine as is

looking at getLocation/geolocate, unless you need a separate geolocate method, they should be able to be combined and simplified to

getLocation() {
    var geolocate = () =>
        new Promise((resolve, reject) => 
            navigator.geolocation.getCurrentPosition(resolve, reject, {
                enableHighAccuracy: true,
                timeout: 20000,
                maximumAge: 1000
            });
        );
    // this function will "retry" the supplied function (fn) cont times
    var limitedPromiseRetry = (fn, cont) => fn().catch(err => cont > 0 ? limitedPromiseRetry(fn, cont-1) : Promise.reject('Max number of geolocation attempts'));
    return limitedPromiseRetry(geolocate, 3);
}
Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
  • Nice and compact solution, it avoids using the double promise. From your response I understand that putting promise inside promise is not a good idea and is not supported? – Peter G. Jan 04 '17 at 13:11
  • no, it is supported, it's just not required in this case – Jaromanda X Jan 04 '17 at 13:32
1

async and await

You can not use await inside functions without the async keyword. So the error occurs because the executor function is not async:

var getLocation = async function(){ // <-- this function has "async" keyword, but ...
    return new Promise( function( resolve, reject ){ // <-- ... this "executor" function has no "async" keyword.
        var value = await geolocate();               // <-- ERROR: await is only valid in async function.
        resolve( value );
    })
};

But you should not make the promise executor an async function.
See https://eslint.org/docs/rules/no-async-promise-executor for more info.

new Promise( async function( resolve, reject ){ // <-- BAD ! Don't do it !
   ...
})

without nested promise

But as getLocation is a promise already, you do not need the nested new Promise( ... ) at all:

var getLocation = async function(){ // <-- "async" makes a function to be a promise
    var value = await geolocate();

    // "return value" inside async function is "the same" as
    // "resolve( value )" in a promise
    return value;
};

So, theoretically, you could solve your problem in the following way (although there are probably better ways of doing it. For 'rejecting' inside async function, see also How to reject in async/await syntax?).

var getLocation = async function(){
    for( let i = 0; i < 3; i++ ){
        try {
            console.log('try ...', i);
            var location = await geolocate(i);
            console.log('... success');
            return location;
        } catch(err) {
            console.log('... next try');
            continue;
        }
    }
    return Promise.reject('no success');
};

getLocation().then(function(result){
    console.log('then:', result);
}).catch(function(reason){
    console.log('error:', reason);
})

nested Promises

Promises inside promises are ok.
Note that resolving promises with resolved promises behaves "the same" as resolving only one promise. You will not notice any difference in the .then() function, no matter if you resolve a value, or a resolved promise inside a promise, ... and so on.

var nestedPromises = new Promise( function( resolve1, reject1 ){
    resolve1( new Promise( function( resolve2, reject2 ){
        resolve2( new Promise( function( resolve3, reject3 ){
            resolve3('resolved value');
        }));
    }));
});

nestedPromises.then( function( value ){
    console.log('value:', value);   // <-- "value: resolved value"
})
kca
  • 4,856
  • 1
  • 20
  • 41