Running heavy task in background (so as to have responsive UI))

#1

Hi guys,
in my app, I am getting some data through websockets and I need to update some charts whenever I get a new chunk. The problem is that it takes a really long time. The code calculates a FIR filter parameters (whatever, not important) which needs to evaluate thousands of logarithms and other non-JS-friendly operations and the whole process is terribly slow and takes about 20 seconds. That’s not cool, but at least I would like to inform the user that the calculation is in progress (I use Ionic LoadingController) so that he does not try to click on anything, but currently it does not work, probably because the calculation is also blocking the UI updates. What I have is this:

    this.ws = new WebSocket('ws://localhost:3001');
    console.log('ws:', this.ws);
        
    this.ws.onmessage = (data) => {
      this.showLoading(true);
      
      console.log(data);
      const d = data.data.split(' ');
      const elems = d[0];
      const size = d[1];
      for (let i = 0; i < elems; i++){
        let fir = [];
        for (let j = 0; j < size; j++){
          fir.push(d[2 + i * size + j]);
        }
        this.setGraphData(fir, i);  // some heavy calculation in here
      }
      this.showLoading(false);
    };

However, what it does is that it takes that 20 seconds and than the LoadingController flashes on and off instantly. I take it that the whole UI process update process happens after the whole computation.

So I tried making it a Promise, but it behaves the same way.

    this.ws = new WebSocket('ws://localhost:3001');
    console.log('ws:', this.ws);
        
    this.ws.onmessage = (data) => {
      this.showLoading(true);
      var p = new Promise((resolve, reject) => {
        console.log(data);
        const d = data.data.split(' ');
        const elems = d[0];
        const size = d[1];
        for (let i = 0; i < elems; i++){
          let fir = [];
          for (let j = 0; j < size; j++){
            fir.push(d[2 + i * size + j]);
          }
          this.setGraphData(fir, i);
        }
        resolve(true);
      });
      p.then((value) => {
        this.showLoading(false);
        console.log('update done');
      });
      //this.showLoading(false);
    };

Just for reference, the function for showing / hiding the loading spinner looks like:

showLoading(show: boolean){
    if (show){
      this.loading = this.loadingController.create({content : "Recalculating..."});
      this.loading.present();
    }
    else this.loading.dismiss();
}

Do I need web workers for this, or did I just use the Promise incorrectly?

Thanks…

#2

I probably know where the problem is with the Promise. The thing is that there is no asynchronous operation within the whole heavy calculation and thus there is no place to “yield” for a while. Is there some way to tell the promise at certain places to switch to the other tasks for a while? (I guess it isn’t). The only other solutions I can think of is:

  • Delay the execution of the promise a bit (I don’t like this - the obvious reasons)
  • Place the computation within a web worker and notify the page when it is done.

//Edit: OK, to track my progress… I have tried putting the heavy lifting in web workers. It seems it would have solved the problem if…

  • I was able to import mathjs library in the worker scripts. I read I need to use importScripts instead if import or require since this is a worker. However it doesn’t seem to work and I always get

importScripts('../../node_modules/mathjs/index.js');
-> NetworkError: Failed to load worker script at …/…/node_modules/mathjs/index.js (nsresult = 0x804b001d)

  • Will this even work when I generate Cordova app from this one? Does it even support web workers? I haven’t been able to find any satisfying answer…

Thanks