我如何才能等到订阅完成后再返回另一个函数的值

时间:2020-06-29 08:23:10

标签: angular typescript service rxjs

我有一个简单的项目,该项目从JSON文件获取用户数据,它将在另一个页面中显示单个用户的数据。 我的“老师”告诉我,我必须使用2个服务来处理用户数据,一个服务用于处理http请求,另一个服务则采用可观察的并执行CRUD操作。

这是HTTP服务

user-Http.service.ts

  constructor(private http:HttpClient) { }

  getBooks() {
    return this.http.get<User[]>(environment.userUrl);
  }

这是用户服务

user.service.ts

 users: User[];
 user: User[] = new User;

  constructor(private userHttp: UsersHttpService) {
    userHttp.getBooks().subscribe(res => {
      this.users = res;
    })
   }

//...

  getUser(id: number) {
    this.userHttp.getBooks().subscribe(res => {
        if (res != null) {
          this.users = res;
          console.log(this.users.find(user => user.id == id))
          this.user = this.users.find(user => user.id == id);
          console.log('Return ', this.user)
          return this.user;
        }
      });
    }
//...

但是当我在用户信息组件中调用getUser函数时,控制台将返回此错误

错误TypeError:无法读取未定义的属性“ find” 在UserService.getUser(user.service.ts:23)

这是用户信息组件

user-info.component.ts

 public user: User
  constructor(private route: ActivatedRoute, private _userService: UserService) { }


  ngOnInit(): void {
    this.route.params.subscribe(
      p => {
        this.user = this._userService.getUser(p.id);
      }
    )
  }

user-info.component.html

{{user | json}}

在许多控制台日志之后,我发现getUser函数在订阅结束之前已执行,因此,它执行对未定义数组的查找并抛出此错误。 我该如何解决?

4 个答案:

答案 0 :(得分:1)

我将userHttps.getBooks()和getUser()结合起来

constructor(private userHttp: UsersHttpService) {}

  getUser(id: number): User {
    userHttp.getBooks().subscribe(res => {
      if (res != null) {
        this.users = res;
        return this.users.find(user => user.id == id);;
      }
      return null;
    });
  }

答案 1 :(得分:0)

您可以使用switchMap之类的内容来嵌套订阅。

这样,您将不会遇到现在面临的问题。

此外,尝试根据您的需求浏览地图,haustMap,concatMap和MergeMap。

我已经使用过地图,因为无法观察到返回,请找到stackblitz

答案 2 :(得分:0)

在HTTP调用获取用户列表之前,有角调用getUser()。要立即修复,请初始化users数组:

  users: User[] = [];

或者在getUser()

中签入
 getUser(id: number) {
     return this.users ? this.users.find(user => user.id == id) : null;
 }

答案 3 :(得分:0)

您可以使用subject来确保仅在从服务中检索到数据后才能获取数据。

您的UserHttpService类未更改,但是在UserService类中添加了组件将订阅的主题

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Subject } from 'rxjs';
import { UserHttpService } from './user-http.service';
import { take } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private user: User;
  public userSelected = new Subject<boolean>();

  constructor(private userHttpServ: UserHttpService) {

  }

  getUserById(id: number) {
    this.userHttpServ.getBooks()
      .pipe(take(1))
      .subscribe((res => {
        this.user = res.results.find(u => u.id == id);
        if (this.people !== undefined) {
          this.userSelected.next(true);
        } else {
          this.userSelected.next(false);
        }
      });
  }

  getUser() {
    return {...this.user };
  }
}

在组件中,您预订了userSelected主题。您调用getUserById,当找到用户时,主题将发出true,并且您的订阅将获取用户数据。

export class UserInfoComponent  {
  public user: User;
  private userSub: Subscription;

  constructor(private userServ: UserService) {
    this.userSub = this.userServ.userSelected.subscribe(res => {
      if (res) {
        this.user = this.userServ.getUser();
      }
    });
  }

  getUserById(id: number) {
    this.userServ.getUser(id);
  }
...
}

别忘了取消ngOnDestroy上的userSub订阅,以避免内存泄漏。

这里是一个例子: https://stackblitz.com/edit/angular-ivy-ag2unt