1

When doing some synchronous actions that contain heavy calculations, showing something before and after might be a problem.

Component template:

<div>
  <my-spinner *ngIf="myService.isLoading"></my-spinner>
</div>

Service

@Injectable()
export class MyService {
    private isLoading = false;
    constructor() {}

    myFunction() {
        this.isLoading = true;
        this.calculateSomethingSuperHeavyThatTakes5seconds();
        this.isLoading = false;
    }
}

executing myFunction actually wouldn't show the spinner.

One way I fixed that was wrapping the heavy function with setTimeout like this:

myFunction() {
    this.isLoading = true;

    setTimeout(() => {
        this.calculateSomethingSuperHeavyThatTakes5seconds();
        this.isLoading = false;
    });
}

And it works but it's not a clean solution for me. applicationRef.tick() in myFunction(), wrapping myFunction content in this.zone.run() or changeDeetctionRef.detectChanges() variations didn't fix the problem.

Tukkan
  • 1,574
  • 2
  • 18
  • 33
  • 2
    You might want to look into using [web workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) to perform the calculation separately, so that it doesn't cause your UI to freeze. Then, you simply listen to a response from the web worker before toggling `this.isLoading` to false again. – Terry Feb 28 '20 at 14:08
  • @Terry unfortunately those are also google maps calculations that cannot be performed in webworkers. – Tukkan Feb 28 '20 at 14:12
  • Since javascript is single threaded, you will need to do something asynchronous, similar to the setTimeout... In the first example, Angular was unable to read `isLoading` because Angular never had a chance to run while `isLoading` was true. – Joseph Dykstra Feb 28 '20 at 14:12
  • What's the reason that you don't want to (or can not) use your second solution? – Joseph Dykstra Feb 28 '20 at 14:13
  • @JosephDykstra I am wondering if it can be done in a different way as setTimeout is not very clean in my opinion – Tukkan Feb 28 '20 at 14:15
  • Use double requestAnimationFrame like here https://stackoverflow.com/questions/57650735/execute-non-function-code-before-a-function-call/57651730#57651730 – Estradiaz Feb 28 '20 at 14:24
  • Possibly `setImmediate`, but it apparently has almost no browser support. https://caniuse.com/#feat=mdn-api_window_setimmediate. `requestAnimationFrame` might be good, like Estradiaz suggested. It's more about graphics, so it is not the cleanest either. Perhaps `Promise.resolve().then( yourFunction )` would work for you. – Joseph Dykstra Feb 28 '20 at 14:29
  • 1
    I don't know much about angular, but is there a way to synchronously force an update to `isLoading`? – Joseph Dykstra Feb 28 '20 at 14:33
  • Updating the DOM is kinda graphics, to queue a microtask is still before DOM render, isn't it? – Estradiaz Feb 28 '20 at 14:38

0 Answers0