Based on my understanding of the question, your code is working "as expected". The flickering is not caused by a bug in your implementation, but instead by a "limit case": two successive $http calls, resulting in your loader screen to go off for a split second after the first request completes just to be reactivated soon after when the second request is made. When the two request are close enough this gives the flickering effect: your end user does not know that two sequential requests are made, he just sees a loading screen going off only to come up again soon after.
In this case, counting the open request cannot mitigate your problem: when the first promise is completed, the second one hasn't been made yet: the counter is still "one open request", so after the promise completes your logic detects that now there are 0 open requests and stops the loading screen.
For those reasons, the only viable solution I can imagine is to implement a sort of "switch off delay" on the loading screen. By making the "loading screen switch off" not immediate, you give your app the time to start the second request. In order to achieve this, you have two options.
The first and probably the cleaner one is to implement the "delay" in the component handling the spinner. Modify the .hideSpinner() method to hide the spinner after a certain time delay has passed, and modify the .displaySpinner() method so that it cancels out any pending "hideSpinner" call. Notice that this may not be viable if you are using a spinner component that you didn't implement and therefore cannot modify easily.
The second approach is to work on the interceptor side. You should already have an "end request" method that checks if the request counter has returned to 0 and in that case stops the loading screen
Your code should look similar to (notice, this uses Typescript):
private startRequest(): void {
// If this is the first request,start the spinner
if (this.requestCount == 0) {
this.loadingOverlayService.start();
}
this.requestCount++;
}
private endRequest(): void {
// No request ongoing, so make sure we don’t go to negative count.
// Should never happen, but it is better to be safe than sorry.
if (this.requestCount == 0)
return;
this.requestCount--;
// If it was the last request, call the service to stop the spinner.
if (this.requestCount == 0) {
this.loadingOverlayService.stop();
}
}
Just add a setTimeout delay on the endRequest method. This way, the actual "is this the last request" check will be delayed, giving your app the time to start a new request before the spinner gets closed. Notice that this introduce a new problem: now any loading spinner will last 1Δ more than required, where Δ is the timeout you are using. In most of the real world cases this isn't actually a problem, you don't want your loading spinner to be "too fast" anyway in order to avoid a similar flickering problem with very short requests.
Your code should then be as follow:
private endRequest(): void {
setTimeout(function() {
if (this.requestCount == 0)
return;
this.requestCount--;
if (this.requestCount == 0) {
this.loadingOverlayService.stop();
}
}, 1000);
}
As already stated, now the check will run a second after the request has ended. This will give your second request the time to be started and increment the counter before the handler can check if there are still other pending request to await. As a result, your loading screen should'b be rapidly closed and reopened but instead just stay open, hence removing the appearance of flickering.
PS: There is a third option that I haven't discussed because your post gave me the impression it won't be applicable to your situation, so I will just post it here in this foot notice as an hint for future readers.
IF all your requests are predetermined, meaning they can be started at the same time (no request must await for the results of the preceding one) you may be able to chain them in a cumulative promise by using $q.all(). This may avoid the flickering effect, but further tests would be required to be sure this solution fits your need. In the end, the setTimeout option is probably the most convenient one, with the best effort/cost/quality compromise.