2017-01-02 7 views
3

У меня есть сценарий, который создает несколько веб-мастеров, эти веб-мастера выполняют определенную работу и отправляют сообщение, когда они заканчивают.Подождите, пока несколько веб-мастеров закончатся

Проблема в том, что мне нужно получить результат от всех из них, а затем вычислить окончательное решение. В других работах они разрабатывают частичное решение проблемы, и основной поток использует эти частичные решения для получения окончательного ответа.

Как можно дождаться окончания всех этих потоков, есть ли что-то в Javascript, например invokeAll в Java?

Более или менее, то, что у меня есть в моей главной теме:

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 

ответ

4

Когда последняя часть данных получена, вызовите функцию вычислительно заместитель окончательного решения:

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

Очевидно, параметризуем, что, по вашему мнению, необходимо, но createWorker в настоящее время не параметрируется, кроме i, поэтому ...

Обратите внимание, что записи в data может быть не в порядке. Рабочий для i == 0 может не заполнить до после работника за i == 1, только из-за капризов планирования потоков или если работа требует большей обработки. Если вам нужны они в порядке, это легко сделать, но мы должны добавить счетчик (или цикл через data на каждом завершении проверить):

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

Если вы хотите более современный, гибкий подход, рассмотреть вопрос об использовании обещания, которые являются родными для JavaScript от ES2015 (аки ES6) и может быть polyfilled для использования на старых двигателях JavaScript:

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

Это также имеет то преимущество, что data будет содержать результаты в порядке, нам не нужно делать эту работу самостоятельно.

Вышеупомянутый раздел соответствует вашему текущему коду, в котором отсутствуют какие-либо положения об ошибках. Но это, как правило, лучше сделать обработку ошибок:

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

@torazaburo: Как делает исходный код. Но я добавлю примечание. –

0

Вы могли promisify рабочих, а затем использовать Promise.all:

Promise.all(Array.from(Array(100), (x, i) => i).map(i => 
    new Promise((resolve, reject) => { 
    const worker = new Worker('js/worker.js'); 
    worker.postMessage(i); 
    worker.addEventListener('message', event => resolve(event.data)); 
    worker.addEventListener('error', reject); 
    })) 
    .then(results => ...); 
+0

Я бы поддержал это, если бы у меня хватило репутации, спасибо вам! – btt

Смежные вопросы