等待多个网络工作者完成

11

我有一个脚本可以创建多个Web Worker,这些Web Worker执行一些任务并在完成后发送消息。

问题是我需要获取它们全部的结果,然后计算最终的解决方案。换句话说,它们会解决问题的一部分,主线程使用这些部分解决方案来生成最终答案。

我应该如何等待所有线程完成呢?在JavaScript中是否有类似Java中的invokeAll的方法?

大概就是在我的主线程中:

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
2个回答

17

当收到最后一份数据时,请调用您的计算最终解决方案的函数:

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的worker可能在 i == 1 的worker完成之后才能完成。如果您需要以有序的方式进行操作,则很容易完成,但我们必须添加一个计数器(或在每次完成时循环遍历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);
}
如果您想要更现代、更灵活的方法,请考虑使用 Promise。自 ES2015(也称为 ES6)起,Promise 已成为 JavaScript 的本地功能,并可用于旧版 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
    });

7
您可以将工作程序转换为Promise对象,然后使用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 => ...);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接