【问题标题】:Wait for several web workers to finish等待几个 web worker 完成
【发布时间】:2017-01-02 17:37:55
【问题描述】:

我有一个创建多个网络工作者的脚本,这些网络工作者执行一些工作并在完成时发送一条消息。

问题是我需要从所有这些中获取结果,然后计算最终解决方案。在其他作品中,他们制定了问题的部分解决方案,主线程使用这些部分解决方案来生成最终答案。

我如何才能等待所有这些线程完成,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

【问题讨论】:

    标签: javascript web-worker


    【解决方案1】:

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

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

    如果您想要更现代、更灵活的方法,请考虑使用 Promise,从 ES2015(又名 ES6)开始,它是 JavaScript 原生的,并且可以通过 polyfill 用于较旧的 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
        });
    

    【讨论】:

    • @torazaburo:原始代码也是如此。但我会添加一个注释。
    【解决方案2】:

    你可以承诺工人,然后使用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 => ...);
    

    【讨论】:

    • 如果我有足够的声誉,我会投赞成票,无论如何谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-01
    • 1970-01-01
    • 2013-04-05
    • 2013-10-23
    • 2018-11-08
    相关资源
    最近更新 更多