Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for several web workers to finish

I have a script that creates several webworkers, these webworkers perform some work and send a message when they finish.

The problem is I need to get the result from all of them and then compute a final solution. In other works, they work out a partial solution of the problem and the main thread uses those partial solutions to generate the final answer.

How could I wait for all those threads to finish, is there something in Javascript like invokeAll in Java?

More or less, what I have in my main thread:

var data = [];

function createWorker(i) {
    var v = new Worker('js/worker.js');
    v.postMessage(i);
    v.onmessage = function(event){
        data.push(event.data);
    };
}

for(var i = 0; i < 100; i++) {
    createWorker(i);
}

//Wait until all have finished somehow
like image 230
btt Avatar asked Jan 02 '17 09:01

btt


People also ask

How many web workers Workers can run concurrently?

How many web workers can run concurrently JavaScript? A web worker is a JavaScript program running on a different thread, in parallel with main thread. The browser creates one thread per tab. The main thread can spawn an unlimited number of web workers, until the user's system resources are fully consumed.

What are web workers example?

Common examples of web workers would be: Dashboard pages that display real-time data such as stock prices, real-time active users, and so on. Fetching huge files from the server.

What is the right method that cancels a web worker?

terminate() The terminate() method of the Worker interface immediately terminates the Worker .


1 Answers

When the last piece of data is received, call your compute-the-final-solution function:

var data = [];

function createWorker(i) {
    var v = new Worker('js/worker.js');
    v.postMessage(i);
    v.onmessage = function(event){
        data.push(event.data);
        if (data.length === 100) {               // <====
            computeFinalSolution();              // <====
        }                                        // <====
    };
}

for(var i = 0; i < 100; i++) {
    createWorker(i);
}

Obviously, parameterize that as you consider necessary, but createWorker isn't currently parameterized other than i, so...

Note that the entries in data may not be in order. The worker for i == 0 might not complete until after the worker for i == 1, just because of the vagaries of thread scheduling or if the work requires more processing. If you need them in order, it's easily done, but we have to add a counter (or loop through data on each completion to check):

var data = [];
var dataReceived = 0;

function createWorker(i) {
    var v = new Worker('js/worker.js');
    v.postMessage(i);
    v.onmessage = function(event){
        data[i] = event.data;                    // <====
        if (++dataReceived === 100) {            // <====
            computeFinalSolution();              // <====
        }                                        // <====
    };
}

for(var i = 0; i < 100; i++) {
    createWorker(i);
}

If you want a more modern, flexible approach, consider using promises, which are native to JavaScript as of ES2015 (aka ES6) and can be polyfilled for use on older JavaScript engines:

function createWorker(i) {
    return new Promise(function(resolve) {
        var v = new Worker('js/worker.js');
        v.postMessage(i);
        v.onmessage = function(event){
            resolve(event.data);
        };
    });
}

var promises = [];
for(var i = 0; i < 100; i++) {
    promises.push(createWorker(i));
}
Promise.all(promises)
    .then(function(data) {
        // `data` has the results, compute the final solution
    });

This also has the advantage that data will contain the results in order, we don't have to do that work ourselves.

The above is in line with your current code, which doesn't appear to have any provision for errors. But it's usually best to do error handling:

function createWorker(i) {
    return new Promise(function(resolve, reject) {
        var v = new Worker('js/worker.js');
        v.postMessage(i);
        v.onmessage = function(event){
            // If you report errors via messages, you'd have a branch here for checking
            // for an error and either calling `reject` or `resolve` as appropriate.
            resolve(event.data);
        };
        // EITHER:
        v.onerror = reject; // Rejects the promise if an error is raised by the web worker, passing along the ErrorEvent
        // OR:
        v.onerror = function(event) {
            // Rejects the promise using the error associated with the ErrorEvent
            reject(event.error);
        };
    });
}

var promises = [];
for(var i = 0; i < 100; i++) {
    promises.push(createWorker(i));
}
Promise.all(promises)
    .then(function(data) {
        // `data` has the results, compute the final solution
    })
    .catch(function(error) {
        // something went wrong
    });
like image 180
T.J. Crowder Avatar answered Sep 19 '22 12:09

T.J. Crowder