问题
When I use a Observable.Interval to perform a http refresh of a UI it locks out the buttons on the UI from working if the interval is too fast. The buttons do not register the click, seems to be a timing issue. If I increase the timing and therefore miss the get call the buttons work, but the data is then delayed in updating.
Interval
this.dataSub = Observable.interval(1000).subscribe(x => {
this.getData();
})
getData
getData(): void {
this.dataService.getData()
.subscribe(
data => this.data = data,
error => console.log("Error HTTP Get Service" + this.data),
() => {});
}
Is there a best practice, or something I'm missing that I should be doing to refresh the UI but not lock out the buttons
回答1:
The theory
In general, you should avoid explicitly subscribing to observables wherever possible. Instead, use all of the operators (yes, it can be very tricky to figure out the right ones) available to compose all the source/input observables into one or more observables that you use in the view with the async pipe.
Don't forget that JS is single threaded (aside from workers). Your UI and most Angular code has to share the one thread, so long running JS locks up your UI.
This has three main benefits:
- It's almost impossible to cause memory leaks. If you don't remember to always unsubscribe in the
ngOnDestroy()hook or when you no longer care about it, you risk creating a memory leak whenever you manually subscribe. Whereas theasyncpipe will unsubscribe correctly when the component/element it is used on gets destroyed - you don't need to worry about it. - Doing less work. Using operators like
switchMap(),switchLatest(), etc, you can cancel and cleanup superceded HTTP calls and other expensive operations, or even stop them before they have started if they are no longer required. Don't do more than you have to. This also usually means the change detection doesn't have to run as much, which means better performance. - Cleaner code. Less member variables, more functional-like code. Yes, it can be a little harder to understand when you are learning Rx, but it gets easier.
In practice
With all that in mind, how could you apply it to your code?
One thing you may not be aware of (many people aren't) is that if your DataService.getData() method is something like:
getData(): Observable<MyData[]> {
return this.http.get('http://some.url/data').map(res => res.json());
}
then every time you subscribe to the observable created by the Http service, a new request is made. This is what you want, but what you don't want is to process the results from any previous requests as soon as a new one is made.
So you could instead just compose one observable with the latest data from the most recent request using something like this in your controller:
ngOnInit() {
// (I follow a convention where observable properties end in $)
this.data$ = Observable.interval(1000).flatMapLatest(() => {
return this.dataService.getData();
});
}
No subscriptions, just a created observable. Then in your view, just use the async pipe with the data$ property and you are golden.
For example:
<ul *ngFor="let d of (data$ | async); trackBy: d?.id">
<li>{{d.name}}</li>
</ul>
来源:https://stackoverflow.com/questions/41557987/angular-2-observable-interval-locks-out-ui