简体   繁体   中英

How to avoid to return an observable inside a promise

I'm using AngularFire to manage user inside my app. I'm still learning about observables and rxjs.

I'm trying to call signInWithEmailAndPassword method - which return a promise - and to retrieve a doc from a collection.

In my service I get the document using this method:

 getDbUser(uid): Observable<User> { return this.angularFirestore.collection<User>(USERS).doc(uid).valueChanges() }

and I login user with:

 login(user: User) { // sign in con email e password return this.angularFireAuth.signInWithEmailAndPassword(user.email, user.password).then((result) => { if(result.user?.uid) { // get user doc return this.getDbUser(result.user.uid); } return null; }); }

With this code, calling login method requires to solve a promise and then subscribe to the observable, like this:

 this.userService.login(this.loginUser).then((userObs) => { userObs.subscribe((user) => { console.log(user); }); })

I would really like to avoid this but I'm not sure how to to do. I tried using RxJs toPromise operator:

 login(user: User) { // sign in con email e password return this.angularFireAuth.signInWithEmailAndPassword(user.email, user.password).then((result) => { if(result.user?.uid) { // recupero l'utente dal db return this.getDbUser(result.user.uid).toPromise(); } return null; }); }

but somehow this is not working (the promise is never resolved).

The reason toPromise() doesn't work because the observable stays open. Use either the first operator or if you're using a later version of rxjs, firstValueFrom . Read more about this at Conversion to Promises from the rxjs documentation.

Since you're using valuesChanges instead of get , then maybe your intention is to keep the observable open. If that's the case then the best thing to do is to convert the login to an observable using from .

login(user: User) {
  return from(this.angularFireAuth.signInWithEmailAndPassword(user.email, user.password)).pipe(
    switchMap(res => (res.user.uid) 
      ? this.getDbuser(res.user.uid)
      : of(null))
  );
}

Try using rxjs operators like mergeMap, swtichMap, pipe, map etc.You can avoid nested subscription with those.

if you want to let know of any obserable consumer that a user just logged in only after signInWithEmailAndPassword() has returend i would decalre a subject and subsscribe to user$ from any service consumers.

private userSubject$:Subject<string>=new Subject<string>();
public user$:Observable<string>=this.userSubject$.asObservable();
    login(user: User) {

    // sign in con email e password
    return this.angularFireAuth.signInWithEmailAndPassword(user.email, user.password)
      .then((result) => {

        if(result.user?.uid) {

          // recupero l'utente dal db
          return this.userSubject$.next(result.user.uid);
        }

        return null;
      });

} and from outside you will use the service:

this.userService.login(this.loginUser);
this.userService.user$.subscribe(user=>console.log);

The first comment is the correct answer:

You can add .pipe(first()) before .toPromise "

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