简体   繁体   中英

Angular 7 - Loading data in chunks slows down the page

Summary

What is the proper Angular 7 way to load a list, and then iterate over that list making http calls to fill out additional data?

Details

I am using Angular 7 sitting on a Node.JS server.

I need to display a list of items. I can get the basic details, say name and picture of an item, very quickly. However, some of the details take a while to compute on the back end - let's say the number of times it has been purchased and the the state which has purchased the most of the item.

The idea is to have the basic information load in the list, and then start at the top and start filling out the complex data.

So what I have 2 APIs.

  1. Return a list of 200+ items with their name, price, and picture (responds in ~500ms).
  2. Given a list of ids, return the complex information for the item (~250ms-2,000ms).

Here is the pseudo code for what I have done:

public ngOnInit() {
   this.service.getList().subscribe(list => {
      this.items = list;
      list.forEach(i => pendingIds.push(i.id));
      this.loadDetails();
   });
}

public pendingIds = [];    
public loadDetails() {
   ids = this.pendingIds.popAFew();
   this.service.getDetails(ids).subscribe(info => {
      this.items[id].properties = info.properties;
      if (pendingIds.length) {
         this.loadDetails();
      }
   });
}

The above code 'works' but the page is jittery / not very responsive until the data all finishes loading.

If I take out the loadDetails() call then the page loads quickly and is responsive immediately.

I am sure that the problem is related to the nesting of observables, which is executed potentially hundreds of times. However, I couldn't find an example online that does what I am trying to do.

I've played around with async pipe and 'rxjs/observable/from', but I haven't found the secret sauce.

Does anyone have examples of how to lazy load information in a list?

Edit

I've tried using flatMap, see the following, but it is still jumpy:

public pendingIds = [];    
public loadDetails() {
   let obs: Observable<any>;
   this.pendingIds.forEach(id => {
      if (!obs) {
         obs = this.service.getDetails(id).pipe(tap((info) => this.loadDetails(info)));
      } else {
         obs = obs.pipe(
            flatMap(() => this.service.getDetails(id)),
            tap((infp) => this.loadDetails(info))
         );
      }
   });
   obs.subscribe(() => {{);
}

Edit 2

3rd way of loading - still is jittery.

let activeRequests = 0;
Observable.create((observer) => {
    const interval = setInterval(() => {
        if (activeRequests < 2) {
            if (this.pendinIds.length > 0) {
                observer.next(this.pendingIds.shift());
            } else {
                clearInterval(interval)
            }
        }
    }, 25);
    return () => clearInterval(interval);
})
.pipe(flatMap(id => { activeRequests++; return this.service.getDetails(id); })
.subscribe(info => { --activeRequests; this.loadDetais(info})

Any ideas?

I solved the slow downs by using OnPush change detection.

Short answer: Start at your leaf components and:

  • Add changeDetection: ChangeDetectionStrategy.OnPush to the component declaration.
  • Add private ChangeDetector: ChangeDetectorRef to the component constructor.
  • Call this.ChangeDetector.markForCheck(); everywhere your data changes in the component.

For a complete explanation, see: https://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html

Anytime you can do so, using OnPush is a good idea but it can lead to tricky complications.

For example, my situation turned out to be a bit more complicated because input properties that are arrays do not trigger change detection when the array elements change (such as augmenting the data).

I solved this by creating a Subject on the parent that is passed to the list item component. Then when I get the additional info I call Subject.next(id of updated item). The list item component is subscribed to the subject and if its id matches the observed id then it calls the same markForCheck() call.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM