Search code examples
javascriptweb-worker

10 webworkers created, but only 9 returning a value


So I'm basically doing a lot of operations on objects in an array. So I decided to use webworkers so I can process them in a parallel manner. However, if I inputted an array with 10 objects, only 9 workers would return a value. So I created this simple mockup that reproduces the problem:

var numbers = [12, 2, 6, 5, 5, 2, 9, 8, 1, 4];
var create = function(number) {
    var source = 'onmessage = function(e) { postMessage(e.data * 3) }';
    var blob = new Blob([source]);
    var url = window.URL.createObjectURL(blob);
    return new Worker(url)
}; 

var newnumbers = [];
for (var i = 0; i < numbers.length; i++) {
    var worker = create();
    worker.onmessage = function(e) {
        newnumbers.push(e.data);
        worker.terminate();
    }

    worker.postMessage(numbers[i]);
}

So basically, each number in de array gets multiplied by 3 and added to a new array newnumbers. However, numbers.length = 10 and newnumbers.length=9. I have debugged this for quite a while and I verified that 10 workers were created.

I feel like i'm doing something stupidly wrong, but could someone explain?

Run it here on JSFiddle


Solution

  • You call terminate on the last worker before it processes the messsage, so the last worker is not outputting anything.

    This is happens because the worker variable is actually a global variable instead of a local one. You can replace var worker with let worker to make it a local variable. If you are worried about let browser compatibility use an Array to store the workers, or simply create a function scope`.

    Now, terminate is called on the last worker because the var worker variable will be set to the last worker when the loop ends. Note that the loop will complete executing before any worker will start processing (as the loop is synchronous code).

    In your original code instead of calling terminate() on each worker you would call 10 times terminate() on the last worker.

    var newnumbers = [];
    for (var i = 0; i < numbers.length; i++) {
        let worker = create();
        worker.onmessage = function(e) {
            newnumbers.push(e.data);
            worker.terminate(); // "worker" refers to the unique variable created each iteration
        }
        worker.postMessage(numbers[i]);
    }
    

    Demo: https://jsfiddle.net/bqf5e9o1/2/