How to wait for multiple WebWorkers in a loop

2019-09-04 06:43发布

问题:

I have the following issue with Web Workers in JS. I have a heavy duty application doing some simulation. The code runs in multiple Web Workers. The main thread is running on a WebPage. But could also be a Web Worker, if it makes sense.

Example:

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

What I need is really the waitUntilWorkersAreDoneButAllowBrowserToReact function or a concept to get this running. Every searching reagarding Mutex, sleep, etc ends in the following sentences: "JS is single threaded", "This will only work if you are not in a loop", "There is no reason to have a sleep function". etc.

Even when passing the main task to another Worker, I got the problem, that this thread is 100 % duty on checking, if the others are ready, which is waste of energy and processing power.

I would love to have a blocking function like myWebWorker.waitForReady(), which would allow events still to be handled. This would bring javascript to its next level. But may be I missed a simple concept that will do exactly this.

Thank you!

回答1:

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

No, that's not possible. All the statements you researched are correct, web workers stay asynchronous and will only communicate by messages. There is no waiting for events, not even on worker threads.

You will want to use promises for this:

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 using a recursive approach.