Considering I have a function which calls a server and returns user data through Observable:
getUserById (id: number): Observable<User> {
return this.http.get('/users/' + id)
.map(response => response.json())
;
}
How do I make it to behave more like a Promise?
The problem is, that the request won't be made until someone subscribes to the returned observable. Also, when multiple subscriptions are done, the server will be called multiple times.
What I want is:
To call server as soon as getUserById()
is called (the subscription should be optional, only if caller really wants to obtain the returned value or cares to know when request is complete)
Multiple subscriptions should not spawn multiple requests, but should return the same value obtained from the server
With this approach I will be able to make this function work:
refreshUser (): Observable<User> {
return repository.getUserById(this.currentUser.id)
.map(user => this.currentUser = user)
;
}
When calling refreshUser()
the request should be made and this.currentUser = user
should be evaluated. If I want, I can subscribe to the returned observable in order to obtain fresh data, e.g:
// Just refreshes the current user
refreshUser();
// Refreshes the current user and obtains fresh data
refreshUser().subscribe(user => console.log('Refreshed user', user));
I've tried to use publish()
, refCount()
and share()
in different combination, but it only helps with the first requirement (i.e. avoiding multiple calls to the server), but how do I make the original observable hot?
After further investigation with different options it seems like solution with making getUserById()
hot will not solve it, because I have two mapping functions on different levels (one in getUserById()
and one in refreshUser()
) and I need both of them to execute. In order to solve this, I need to somehow "push" the observable from bottom to top. And it looks like it will require a lot of boilerplate code for each level.
In promises it will look pretty simple:
getUserById (id: number): Promise<User> {
return this.http.get('/users/' + id)
.map(response => response.json())
.toPromise()
;
}
refreshUser (): Promise<User> {
return repository.getUserById(this.currentUser.id)
.then(user => {
this.currentUser = user;
return user;
})
;
}
Is there a simpler solution with RxJS or Promises are actually better suited for the job?
From your description it looks like you could do something like this:
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;
}
If you subscribe to the same Observable returned from getUserById
multiple times you'll always get the same result.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With