Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular and Observale: how to avoid multiple requests to API within a given time

I have something like this in an Angular 4 app (for the sake of the example, I have removed code)

@Injectable()
export class SomeService {

  constructor(
    private http: Http
    ) {
  }

  get(id: number) {
    return this.http.get('http://somedomain/somemodel/${id}.json');
  }

}

this is used by some components to make API calls.

constructor(private someService: SomeService) {}
...
someMethod() {
  // code here...
  this.someService.get(2).subscribe( someHandlerFunction );
}

someOtherMethod() {
  // more code here...
  this.someService.get(2).subscribe( someHandlerFunction );
}

the problem is that I don't know when someMethod() and someOtherMethod() will be called. Sometimes both of them may be called and then, my API will be called twice. What I am trying to find is if there any way to change my Service to do this request only after an X amount of time. I tried to use debounce:

get(id: number) {
  return this.http.get(`http://somedomain/somemodel/${id}.json`).debounceTime(10000);
}

expecting that (in this case) this HTTP get request will only be repeated after 10 seconds, if the request is made within this 10 seconds, the request won't repeat but the observable emits the last value. But this didn't work. Any tips?

PS: I know I could control this using some kind of flag, but I can't do this as long I this does not scale very good. I have multiple services with a lot of HTTP requests.

Any thoughts about this?

like image 778
Christian Benseler Avatar asked Jul 21 '17 11:07

Christian Benseler


2 Answers

Debounce should be used on the observable that calls the http service rather than on the http request itself. So for instance :

someObservable
  .valueChanges
  .debounceTime(1000)
  .subscribe(// Call the http service here)

A much better explanation was provided on a previously asked question here.

like image 124
James Byrne Avatar answered Oct 18 '22 09:10

James Byrne


You can cache your results with RxJS and then set an interval that will clear the cache every few seconds, allowing a new request to be made. This is typically a good approach when you don't want users to re-query your database every time they load a page, especially is nothing new has been added.

This is a slightly more verbose approach than the other answer, but it provides a couple of benefits:

  • You are cacheing results, limiting load on your server and database
  • Unlike the other answer using debounceTime only, you are not just blocking the requests. You are blocking the requests AND giving the cached results. If you only block the requests, you will get no results back. With my approach, you still get results if you need them, using the last results that were fetched -- it just won't make a new request until you allow it to.

Cacheing results:

Class ProjectService {
    private cachedProjects: Observable<Project[]>;

    all(): Observable<Project[]> {
        if (!this.cachedProjects) {
            this.cachedProjects = this.httpService.get(`${this.url}/project`)
                .map(res => res.data.map(project => new Project(project)))
                .publishReplay()
                .refCount();
        }

        return this.cachedProjects;
    }
}

When you call the service:

this.projectService.all().subscribe((projects: Project[]) => {
    // 'projects' will be fetched the first time this is called, 
    // and any subsequent requests will use the 'cachedProjects'
    // stored in the service, until the interval sets it to null.
});

Now to make sure it only blocks the HTTP calls every so often, you can set an interval on your service. Modify your service as follows:

Class ProjectService {
    private cachedProjects: Observable<Project[]>;

    constructor() {
        // Put this in a function or something.
        setInterval(() => {
            this.cachedProjects = null;
        }, 5000);
    }

    all(): Observable<Project[]> {
        if (!this.cachedProjects) {
            this.cachedProjects = this.httpService.get(`${this.url}/project`)
                .map(res => res.data.map(project => new Project(project)))
                .publishReplay()
                .refCount();
        }

        return this.cachedProjects;
    }
}

So now you are allowed to make a new request every 5 seconds, otherwise cached projects will be used.

like image 42
Lansana Camara Avatar answered Oct 18 '22 09:10

Lansana Camara