6

I am working with ionic/angular and RXJS observables.

I am trying to refactor my code using Rxjs observables and i have the next Code :

ionViewWillEnter() {
    if (this.platform.is('core') || this.platform.is('mobileweb')) {
      this.lat = 37.3675506;
      this.lng = -6.0452695;
      this.printMap();
    } else {
      if (this.platform.is('android')) {       
        this.tryGeolocation();            
      } else if (this.platform.is('ios')) {
        console.log("IOS")
      }
    }
  }

If user access from android mobile i should check : isLocationAuthorized, isLocationEnabled() to get the current position with getCurrentPosition(), then i have to print Map where I use Observables forkjoin.

The problem is to check the methods returns promises and I dont know how to chain this flow .

tryGeolocation is the next :

 async tryGeolocation() {
    try {
      if (await this.diagnostic.isLocationAuthorized()) {
        if (await this.diagnostic.isLocationEnabled()) {
          this.loading = this.loadingCtrl.create({
            content: 'Localizando...',
            dismissOnPageChange: true
          });
          this.loading.present();
          const {coords} = await this.geolocation.getCurrentPosition();
          this.lat = coords.latitude;
          this.lng = coords.longitude;
          this.loading.dismiss();
          alert(this.lat);
          alert(this.lng);
          this.printMap();
        } else {
         console.log("error1")
        }
      } else {
console.log("error2")
      }
    } catch (e) {
      console.log('Error getting location', e);
    }
  }


printMap() {
    let obs1 = this._sp.getLocationsByPosition(this.lat, this.lng);
    let obs2 = this._sp.getUserFavoriteLocations2();
    this.subscription = forkJoin([obs1, obs2]).subscribe(results => {
      this.allLocations = results[0];
      this.myLocations = results[1];
      this.allLocations = this.allLocations.filter(item => !this.myLocations.some(other => item.id.sid_location === other.id.sid_location && item.id.bid_environment === other.id.bid_environment));   

      this.map = new google.maps.Map(this.mapElement.nativeElement, {
        zoom: 13,
        center: {lat: parseFloat(this.lat), lng: parseFloat(this.lng)},
        zoomControl: true,
        draggable: true            
      });

      new google.maps.Marker({
        position: {lat: parseFloat(this.lat), lng: parseFloat(this.lng)},
        map: this.map,
        icon: {
          url: "https://maps.gstatic.com/mapfiles/api-3/images/spotlight-poi2_hdpi.png"
        }
      });
      this.printMarkers();
    });
  }

I've tried to convert promise to observables like this :

let obs1 = Observable.fromPromise(this.diagnostic.isLocationAuthorized());
    let obs2 = Observable.fromPromise(this.diagnostic.isLocationEnabled());
    let obs3 = Observable.fromPromise(this.geolocation.getCurrentPosition());

    obs1.flatMap(() => obs2)
      .flatMap(() => obs3)
      .subscribe(coords => {
        console.log(coords);
//CALL TO printMap?
      })

Could someone helpe me to achieve this flow refactoring my code ?

Thank you in advance

SDLUJO
  • 143
  • 1
  • 10
  • Could you use `Promise.all()`? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all – Alex K Jan 15 '19 at 15:15
  • Could you maybe simplify this example to something reproducible? https://stackoverflow.com/help/mcve – martin Jan 15 '19 at 15:23
  • @AlexK I dont think so, because as you can see on tryGeoLocation one depends to other – SDLUJO Jan 15 '19 at 18:04

2 Answers2

0

Rxjs observables and Promises . Defer docs.

Having said that, fromPromise is deprecated, it no longer appears in the documentation. Use from to get an observable from a promise.

import { from } from 'rxjs';

let obs1 = from(this.diagnostic.isLocationAuthorized());
let obs2 = fromPromise(this.diagnostic.isLocationEnabled());
let obs3 = fromPromise(this.geolocation.getCurrentPosition());

But think about if it worth it to your use case. This answer would help you to decide.

It turns out you can mix async-await with observables, but it does not mean it gonna fit your use case. (use this code with caution)

import { defer } from 'rxjs';
defer(async function() {
  const a = await promiseDelay(1000).then(() => 1);
  const b = a + await promiseDelay(1000).then(() => 2);
  return a + b + await promiseDelay(1000).then(() => 3);
})
.subscribe(x => console.log(x)) // logs 7
Luillyfe
  • 6,183
  • 8
  • 36
  • 46
  • 2
    You wrote « Async-await and Observables do not work together. » and the page you linked says « RxJS has, from inception, had a high degree of interoperability with Promises. », proceeding to explain in great detail how they *do* work well together... – hugo Aug 06 '19 at 21:08
0

You can use from and combineLatest to rxjs-ify your promise chains. I posted a stackblitz example here - https://stackblitz.com/edit/rxjs-jnrj37?devtoolsheight=60

basically something like this:

import { combineLatest, from, of } from "rxjs";
import { map } from "rxjs/operators";

const promiseMaker = timeInMS =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("promise done");
      resolve("foo");
    }, timeInMS);
  });

const promise1$ = from(promiseMaker(300));

const promise2$ = from(promiseMaker(500));

const promise3$ = from(promiseMaker(800));

combineLatest(promise1$, promise2$, promise3$).subscribe(() =>
  console.log("Ready!!!")
);

if the promises depend on each other, then instead of combineLatest you can use switchMap or mergeMap and chain them as needed

PM1
  • 151
  • 5