Angular2/4 : Refresh Data Realtime

2020-02-07 17:22发布

I need to refresh the data in a component page in an interval. Also I need to refresh the data after doing some action. I am using Obeservables in the service so that I can subscribe to when the response is ready. I am pushing the subscriptions to a object so that I can clear that on ngDestroy, I think, I have the following methods to achieve the same.

Method 1 : setInterval

I have set an interval on ngOnInit, which will call the refreshData in equal interval. The interval object will be cleared using clearInterval in ngOnDestroy method.

export class MyComponent implements OnInit, OnDestroy {
    private subscription: Subscription = new Subscription();

    data: any;
    interval: any;

    ngOnInit() {
        this.refreshData();
        this.interval = setInterval(() => { 
            this.refreshData(); 
        }, 5000);
    }

    ngOnDestroy() {
        this.subscription.unsubscribe();
        clearInterval(this.interval);
    }

    refreshData(){
        this.subscription.add(
            this.myService.getData()
                .subscribe(data => {
                    this.data = data;
                })
        );
    }

    doAction(){
        this.subscription.add(
            this.myService.doAction()
                .subscribe(result => {
                    if(result === true){
                        this.refreshData();
                    }
                })
        );
    }
}

Q1 : On each refresh call a subscription will be added to the subscription object, will that increase the memory usage and may crash the browser if user keeps the page opened for a while?

Method2 : Observable.timer

This method is using a timer which will created after the data is refreshed.

export class MyComponent implements OnInit, OnDestroy {
    private subscription: Subscription = new Subscription();

    data: any;

    ngOnInit() {
        this.refreshData();
    }

    ngOnDestroy() {
        this.subscription.unsubscribe();
    }

    refreshData(){
        this.subscription.add(
            this.myService.getData()
                .subscribe(data => {
                    this.data = data;
                    this.subscribeToData();
                })
        );
    }

    subscribeToData(){
        this.subscription.add(
            Observable.timer(10000).subscribe(() => this.refreshData())
        );
    }

    doAction(){
        this.subscription.add(
            this.myService.doAction()
                .subscribe(result => {
                    if(result === true){
                        this.refreshData();
                    }
                })
        );
    }
}

Q2 : I have the same question(Q1) here. This way, will add the timers also to the subscription object, so infact the subscriptions in the subscription object is doubled.

Q3 : To refresh data after the action method - doAction(), the refreshData is called. So will that create another chain of timer?

Q4 : Which is the better way without memory leaks or if there exists any other way?

4条回答
男人必须洒脱
2楼-- · 2020-02-07 17:45

I am just extending @SpaceFozzy's answer a little bit so that it will be helpful for future visitors here. For refreshing the data, I have used the way of @SpaceFozzy. But for subscriptions, I got a better approach. Please have a look at the answer - https://stackoverflow.com/a/41177163. So I have updated my service and component as follows. Hope this helps.

My Component

import { Subject } from 'rxjs/Subject';

export class MyComponent implements OnInit, OnDestroy {
    private unsubscribe: Subject = new Subject();

    data: any;
    interval: any;

    ngOnInit() {
        this.refreshData();
        if(this.interval){
            clearInterval(this.interval);
        }
        this.interval = setInterval(() => {
            this.refreshData();
        }, 10000);


        this.myService.data$.takeUntil(this.unsubscribe)
            .subscribe(data => {
                this.data = data;
            });

    }

    ngOnDestroy() {
        this.unsubscribe.next();
        this.unsubscribe.complete();
    }

    refreshData(){
        this.myService.updateData()
            .takeUntil(this.unsubscribe)
            .subscribe();
    }

    doAction(){
        this.subscription.add(
            this.myService.doAction()
                .subscribe(result => {
                    if(result === true){
                        this.refreshData();
                    }
                })
        );
    }
}

My Service

import { Observable, BehaviorSubject } from 'rxjs';

@Injectable()
export class MyService {
    private dataSubject: BehaviorSubject<YourDataModel[]> = new BehaviorSubject([]);

    data$: Observable<YourDataModel[]> = this.dataSubject.asObservable();

    updateData(): Observable<any>  {
        return this.getData().do((data) => {
            this.dataSubject.next(data);
        });
    }

    // My data is an array of model objects
    getData(): Observable<YourDataModel[]>{
        return this.http.get('/path')
            .map((response: Response) => {
                let data = response.json() && response.json().your_data_objects;
                if(data){
                    return data;
                }
            })
    }

}
查看更多
等我变得足够好
3楼-- · 2020-02-07 17:47

You should be able to do this without problems:

ngOnInit() {
    this.refreshData();
    this.interval = setInterval(() => { 
        this.refreshData(); 
    }, 5000);
}

refreshData(){
    this.myService.getData()
        .subscribe(data => {
            this.data = data;
        })
    );
}

As per this post Angular will take care of cleaning up after itself.

However, if you're going to have a live data stream in your app I'd suggest changing your component so that rather than subscribing to each response of your service's http request, you instead subscribe once to a new observable data$ property of your service in your component's ngOnInit(). Then, on interval (as you're doing) call updateData() on your service (or setup the interval inside your service) but don't subscribe. When your service successfully fetches the data, it pushes the next value to its observable data$ property, giving you a stream of data from your service that you can react to anywhere in your app.

ngOnInit() {
    this.myService.data$.subscribe(data => { // subscribe once to the data stream
        this.data = data;
    })

    this.refreshData();
    this.interval = setInterval(() => { 
        this.refreshData(); 
    }, 5000);
}

refreshData(){
    this.myService.updateData(); // simply signal for the service to update its data stream
}

With myService.data$ being an observable BehaviourSubject updated in your service, something like this:

public data$: BehaviorSubject<any> = new BehaviorSubject({});

updateData() {
    let data = this.http.get('http://www.data.com').map((data)=>{
        return data.json();
    }).do((data)=>{
        this.data$.next(data);
    })
}

That way you can avoid multiple subscriptions and make the data stream available to any component that needs it.

查看更多
何必那么认真
4楼-- · 2020-02-07 17:52

Just run this command in your project folder for linux user:

sudo sysctl fs.inotify.max_user_watches=524288 
sudo sysctl -p --system
查看更多
Luminary・发光体
5楼-- · 2020-02-07 17:55

Please don't forget to clearInterval on ngDestroy.

    ngOnDestroy() {
        this.unsubscribe.next();
        this.unsubscribe.complete();
        clearInterval(this.interval);
    }
查看更多
登录 后发表回答