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
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
});