如何在循环中等待多个 WebWorker

How to wait for multiple WebWorkers in a loop

我对 JS 中的 Web Worker 有以下问题。我有一个重型应用程序在做一些模拟。代码在多个 Web Worker 中运行。 主线程是网页上的 运行。但如果有意义的话,也可以是 Web Worker。

示例:

    var myWebWorkers = [];
    function openWorker(workerCount){
        for(var i = 0; i < workerCount; i++){
            myWebWorkers[i] = new Worker('worker.js');
            myWebWorkers[i].onmessage = function(e){
                this.result = e.data;
                this.isReady = true;
            }
        }
    }

    function setWorkerData(somedata){
        // somedata.length is always a multiple of myWebWorkers.length
        var elementCntPerWorker = somedata.length / myWebWorkers.length;
        myWebWorkers.forEach(function(worker, index){
            worker.isReady = false;
            worker.postMessage(
                somedata.slice(index * elementCntPerWorker, 
                    (index + 1) * elementCntPerWorker - 1));
        });
    }

    var somedata = [...];
    openWorker(8); 
    for(var i = 0; i < 10000; i++){
         setWorkerData(somedata);
         waitUntilWorkersAreDoneButAllowBrowserToReact(myWebWorkers);
         if(x % 100) updateSVGonWebPage
    }

    function waitUntilWorkersAreDoneButAllowBrowserToReact(){
        /* wait for all myWebWorkers-onchange event, but
           allow browser to react and don't block a full Web Worker 
           Following example is my intension. But will not work, because 
           events are not executed until code excution stops.
        */
        somedata = [];
        for(var i = 0; i < myWebWorkers.length; i++){
            while(!myWebWorkers[i].isReady);
            somedata = somedata.concat(myWebWorkers.result);
        }
    }

我真正需要的是 waitUntilWorkersAreDoneButAllowBrowserToReact 函数或概念来获得这个 运行。每个关于 Mutex、sleep 等的搜索都以以下句子结束:"JS is single threaded"、"This will only work if you are not in a loop"、"There is no reason to have a sleep function"。等等

即使将主要任务传递给另一个 Worker,我也遇到了问题,如果其他线程都准备好了,这个线程 100% 负责检查,这是浪费能源和处理能力。

我希望有一个像 myWebWorker.waitForReady() 这样的阻塞函数,这样事件仍然可以得到处理。这将使 javascript 更上一层楼。但可能是我错过了一个简单的概念,可以做到这一点。

谢谢!

I would love to have a blocking function like myWebWorker.waitForReady()

不,那不可能。您研究的所有陈述都是正确的,网络工作者保持异步并且只会通过消息进行通信。没有等待事件,甚至在工作线程上也没有。

您将需要为此使用承诺:

function createWorkers(workerCount, src) {
    var workers = new Array(workerCount);
    for (var i = 0; i < workerCount; i++) {
        workers[i] = new Worker(src);
    }
    return workers;
}
function doWork(worker, data) {
    return new Promise(function(resolve, reject) {
        worker.onmessage = resolve;
        worker.postMessage(data);
    });
}
function doDistributedWork(workers, data) {
    // data size is always a multiple of the number of workers
    var elementsPerWorker = data.length / workers.length;
    return Promise.all(workers.map(function(worker, index) {
        var start = index * elementsPerWorker;
        return doWork(worker, data.slice(start, start+elementsPerWorker));
    }));
}

var myWebWorkers = createWorkers(8, 'worker.js');
var somedata = [...];
function step(i) {
    if (i <= 0)
        return Promise.resolve("done!");
    return doDistributedWork(myWebWorkers, somedata)
    .then(function(results) {
        if (i % 100)
            updateSVGonWebPage();
        return step(i-1)
    });
}
step(1000).then(console.log);

Promise.all does the magic of waiting for concurrently running results, and the step function does the asynchronous looping 使用递归方法。