3

I wonder if it's possible to wrap Geolocation.watchPosition() https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition in a Promise and use it with async/await idioms in a way it does it's work; constantly returns positions whenever a device's location changes and invokes succeeding functions.

// Example Class
class Geo {
  // Wrap in Promise
  getCurrentPosition(options) {
    return new Promise((resolve, reject) => {
      navigator.geolocation.getCurrentPosition(resolve, reject, options)
    })
  }

  // Wrap in Promise
  watchCurrentPosition(options) {
    return new Promise((resolve, reject) => {
      navigator.geolocation.watchPosition(resolve, reject, options)
    })
  }

  // Works well.
  async currentPosition() {
    try {
      let position = await this.getCurrentPosition()
      // do something with position.     
    }
    catch (error) {
      console.log(error)
    }
  }

  // Any way...?
  async watchPosition() {
    try {
      let position = await this.watchCurrentPosition()
      // do something with position whenever location changes. 
      // Invoking recursively do the job but doesn't feel right.
      watchPosition()
    }
    catch (error) {
      console.log(error)
    }
  }
}
a better oliver
  • 26,330
  • 2
  • 58
  • 66
tkymtk
  • 2,695
  • 3
  • 21
  • 37
  • Something like [this Observables proposal](https://github.com/tc39/proposal-observable/blob/master/README.md)? – gyre Mar 02 '17 at 08:43
  • This *could* be done, but promises are ideal for things that need to happen once. A listener model -- such as an Observable -- would be much more obvious. – lonesomeday Mar 02 '17 at 10:55

1 Answers1

0

Not yet.

The pattern you're describing is an Observable - there isn't language support for it in Javascript, but it's coming.

In ES2015 we got generators: function* & yield, which allowed Iterators - pulling each yield with a for...of loop.

Generators also support push Observers, with var valToGet = yield foo; and generator.next(valToPush); syntax.

Generators are synchronous - they're just passing a single thread back and forth.

In ES2017 we got async & await - these use generators under the covers to convert each await in an async function into a yield new Promise(.... The async function becomes a iterator of promises.

Ideally we would be able to do something like this:

async watchPosition*() {
    try {
        while(this.enabled) {
            // Loop each time navigator.geolocation.watchPosition fires success
            const position = await this.watchCurrentPosition();

            // Pass back to the listener until generator.next
            const atDestination = yield position;
            if(atDestination)
                break; // We're here, stop watching
        }
    }
    catch (error) {
        console.log(error)
    }
}

Unfortunately, async function* isn't supported yet - functions can be generators or async, but not both. There also isn't the nice for...of syntax like there is for iterators, just the clunky generator.next(pushValue), so consuming this hypothetical method is a little ugly:

async listener() { 
    const generator = Geo.watchPosition();
    let item = await generator.next(false);
    while (!item.done) {
        // Update UI
        const atDestination = areWeThereYet(item.value);
        item = await generator.next(atDestination);
    }
}

So asynchronous iterators/observables are coming, but there's lots to sort out first.

In the mean time there are some exceptional libraries that support observer patterns and are available now, such as RxJS.

Keith
  • 150,284
  • 78
  • 298
  • 434