简体   繁体   English

如何避免在 promise 中返回 observable

[英]How to avoid to return an observable inside a promise

I'm using AngularFire to manage user inside my app.我正在使用AngularFire来管理我的应用程序中的用户。 I'm still learning about observables and rxjs.我还在学习 observables 和 rxjs。

I'm trying to call signInWithEmailAndPassword method - which return a promise - and to retrieve a doc from a collection.我正在尝试调用signInWithEmailAndPassword方法 - 它返回 promise - 并从集合中检索文档。

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:使用此代码,调用登录方法需要解决一个 promise 然后订阅 observable,如下所示:

 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:我尝试使用 RxJs toPromise运算符:

 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).但不知何故,这不起作用(promise 从未解决)。

The reason toPromise() doesn't work because the observable stays open. toPromise()不起作用的原因是 observable 保持打开状态。 Use either the first operator or if you're using a later version of rxjs, firstValueFrom .使用第一个运算符,或者如果您使用的是更高版本的 rxjs, firstValueFrom Read more about this at Conversion to Promises from the rxjs documentation.阅读 rxjs 文档中的Promises 转换以了解更多信息。

Since you're using valuesChanges instead of get , then maybe your intention is to keep the observable open.由于您使用valuesChanges而不是get ,那么您的意图可能是保持可观察的开放。 If that's the case then the best thing to do is to convert the login to an observable using from .如果是这种情况,那么最好的办法是使用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.尝试使用 rxjs 运算符,例如 mergeMap、swtichMap、pipe、map 等。您可以避免使用这些运算符进行嵌套订阅。

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.如果您想让任何可观察的消费者知道用户仅在 signInWithEmailAndPassword() 已返回后才登录,我将贴上主题并从任何服务消费者订阅 user$。

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 "您可以在 .toPromise 之前添加.toPromise .pipe(first())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM