让可观察的行为像RxJS中的承诺一样?

时间:2017-06-22 01:34:21

标签: angular rxjs reactivex

考虑到我有一个调用服务器并通过Observable返回用户数据的函数:

getUserById (id: number): Observable<User> {
  return this.http.get('/users/' + id)
    .map(response => response.json())
  ;
}

如何让它更像Promise?

问题是,在有人订阅返回的observable之前不会发出请求。此外,当完成多个订阅时,将多次调用服务器。

我想要的是:

  • 调用getUserById()后立即调用服务器(订阅应该是可选的,只有当调用者真的想要获取返回的值或者想知道请求何时完成时)

    < / LI>
  • 多个订阅不应产生多个请求,但应返回从服务器获取的相同值

通过这种方法,我将能够使这个功能起作用:

refreshUser (): Observable<User> {
  return repository.getUserById(this.currentUser.id)
    .map(user => this.currentUser = user)
  ;
}

调用refreshUser()时,应该发出请求并评估this.currentUser = user。如果我愿意,我可以订阅返回的observable以获取新数据,例如:

// Just refreshes the current user
refreshUser();

// Refreshes the current user and obtains fresh data
refreshUser().subscribe(user => console.log('Refreshed user', user));

我尝试在不同的组合中使用publish()refCount()share(),但它只对第一个要求有帮助(即避免多次调用服务器),但是如何我可以将原始的可观察性变得热吗?

更新

使用不同的选项进行进一步调查之后,似乎使getUserById()热的解决方案无法解决它,因为我在不同级别上有两个映射函数(getUserById()中的一个和{{1}中的一个})我需要它们两个来执行。为了解决这个问题,我需要以某种方式“推动”观察者从下到上。看起来每个级别都需要很多样板代码。

在承诺中,它看起来很简单:

refreshUser()
getUserById (id: number): Promise<User> {
  return this.http.get('/users/' + id)
    .map(response => response.json())
    .toPromise()
  ;
}

是否有一个更简单的解决方案,RxJS或Promise实际上更适合这项工作?

2 个答案:

答案 0 :(得分:2)

根据您的描述,您似乎可以执行以下操作:

getUserById (id: number): Observable<User> {
  const observable = this.http.get('/users/' + id)
    .map(response => response.json())
    .publishReplay(1)
    .refCount()
    .take(1);

  observable.subscribe();
  return observable;
}

如果您多次订阅从getUserById返回的相同Observable,您将始终获得相同的结果。

答案 1 :(得分:1)

您似乎需要使用或SubjectBehaviorSubject而不是正常的Oberservable。在您的情况下,BehaviorSubject更好,因为它存储当前值。

声明两个变量,一个是普通User类型,另一个是BehaviorSubject User

currentUser:User;
userBSubject:BehaviorSubject<User>;

因为您需要BehaviorSubject的初始值,所以可以在构造函数中初始化它:

constructor(){
  this.userBSubject = new BehaviorSubject<User>(this.currentUser)
}

所以这就是魔术的来源。每次你需要修改(在你的情况下,#34;刷新&#34;)数据时,你会调用方法.next()

refreshUser() {
    return repository.getUserById(this.currentUser.id)
      .subscribe(user => {
        this.userBSubject.next(user);
        return this.userBSubject.asObservables()
      });
}

然后你可以这样做:

// Just refreshes the current user
refreshUser();

// Refreshes the current user and obtains fresh data
refreshUser().subscribe(user => console.log('Refreshed user', user));