Question

I'd like to run a task on a Meteor client which is resource hungry in the background and keep the interface responsive for the user in the meantime. The task does some math (for example finding prime numbers like described here: https://stackoverflow.com/a/22930538/2543628 ).

I've tried to follow the tips from https://stackoverflow.com/a/21351966 but still the interface always "freezes" until the task is complete.

setTimeout, setInterval and those packages like in my current approach also didn't help:

var taskQueue = new PowerQueue();
taskQueue.add(function(done) {
    doSomeMath();
    // It's still blocking/freezing the interface here until done() is reached
    done();
});

Can I do something to make the interface responsive during doSomeMath() is running or am I doing something wrong (also it doesn't look like there is much you could do wrong in PowerQueue)?

Was it helpful?

Solution

JavaScript libraries which solve the problem of asynchronous queuing, assume that the tasks being queued are running in a concurrent but single-threaded environment like node.js or your browser. However, in your case you need more than just concurrency - you need multi-threaded execution in order to move your CPU-intensive computation out of your UI thread. This can be achieved with web workers. Note that web workers are only supported in modern browsers, so keep reading if you don't care about IE9.

The above article should be enough to get you started, however it's worth mentioning that the worker script will need to be kept outside of your application tree so it doesn't get bundled. An easy way to do this is to put it inside of the public directory.

Here is a quick example where my worker computes a Fibonacci sequence (inefficiently):

public/fib.js

var fib = function(n) {
  if (n < 2) {
    return 1;
  } else {
    return fib(n - 2) + fib(n - 1);
  }
};

self.addEventListener('message', (function(e) {
  var n = e.data;
  var result = fib(n);
  self.postMessage(result);
  self.close();
}), false);

client/app.js

Meteor.startup(function () {
  var worker = new Worker('/fib.js');
  worker.postMessage(40);
  worker.addEventListener('message', function(e) {
    console.log(e.data);
  }, false);
});

When the client starts, it loads the worker and asks it to compute the 40th number in the sequence. This takes a few seconds to complete but your UI should remain responsive. After the value is returned, it should print 165580141 to the console.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top