1

The accepted solution here didn't work for me. I am making a call to a location service that needs to be synchronous because an api call is executed on it immediately thereafter.

My logging indicates that the location service is still returning undefined despite the await clause.

service that makes api call

...
@Injectable({providedIn: 'root'})
class PrepopulateService {
  constructor(private locationService: LocationService,
              private log: LoggerService) { }

  async prepopulate(): Promise<boolean> {
    const coords: string[] = await this.locationService.getLocation();
    console.log(coords)
    if(coords == null) {
      return false;
    }
    console.log(coords)
    // api call here
    return true;
  }
}

export { PrepopulateService }

service that gets location for it

...
@Injectable({providedIn: 'root'})
class LocationService {

  constructor(private log: LoggerService) { }

  getLocation(): Promise<string[]> {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition((position) => {
        const longitude = position.coords.longitude;
        const latitude = position.coords.latitude;
        console.log([String(latitude), String(longitude)])
        return [String(latitude), String(longitude)];
      });
    } else {
      this.log.warn('No support for geolocation');
      return null;
    }
  }
}

export { LocationService }

What's wrong with my implementation of async/await?

notacorn
  • 3,526
  • 4
  • 30
  • 60
  • From the looks of it, it seems you forgot return infront of `navigator.geolocation.getCurrentPosition((position) => {` That getLocation doesn't return anything so if you try to use a value from it, it will give you undefined. – Inge Olaisen Feb 23 '20 at 21:22
  • getting TS2332 type void not assignable to promise string, i think its a void method – notacorn Feb 23 '20 at 21:25
  • is there a stateful solution, in light of that? – notacorn Feb 23 '20 at 21:27
  • 1
    yeah you are getting that because of the if. All return paths doesn't return a promise according to the way you set it up. – Inge Olaisen Feb 23 '20 at 21:31

1 Answers1

2

You're not returning a promise from your getLocation function.

You should call navigator.geolocation.getCurrentPosition from within a promise and return that promise. You then resolve the promise within the callback you pass to getCurrentPosition.

getLocation(): Promise<string[]> {
  return new Promise<string[]>((resolve, reject) => {
    if (!navigator.geolocation) {
      reject(Error('No support for geolocation'));
      return;
    }

    navigator.geolocation.getCurrentPosition((position) => {
      const longitude = position.coords.longitude;
      const latitude = position.coords.latitude;
      resolve([latitude.toString(), longitude.toString()]);
    });
  });
}

DEMO: https://stackblitz.com/edit/angular-r6kq9q (with mocked version of getCurrentPosition)

Kurt Hamilton
  • 12,490
  • 1
  • 24
  • 40
  • Always reject with an `Error()` – Roamer-1888 Feb 25 '20 at 00:41
  • @Roamer-1888 Can you provide a source for this. I'm happy to edit, but can't find any definite info on this. My example matches case 3 from this site: https://mrcoles.com/javascript-promises-and-errors/ Not that I'm saying mrcoles.com is the authoritative source on promise rejection! – Kurt Hamilton Feb 25 '20 at 07:00
  • 1
    Kurt, it's just common sense. By rejecting/throwing a proper Error object, your deliberate throws emulate unexpected throws, thus the catch handler is unambiguously passed an Error (or RangeError etc), and (1) the need for exception handling in the catch handler is avoided; (2) the stack trace is made available. – Roamer-1888 Feb 25 '20 at 10:47
  • And not just for Promises. Same applies to synchronous throw/catch. – Roamer-1888 Feb 25 '20 at 11:12