Search code examples
javascriptmultithreadingweb-worker

If i send multiple messages to the same webworker, does it queue them up and process them sequentially?


As the title says...

Basically, if I have a single webworker and post it 1000 messages at once.

Each message results in the worker performing a processing intensive operation.

Am I best to post each message to the webworker sequentially after the previous one completes, or can I safely send all the requests over to the worker knowing that they will just be processed and returned one by one as they complete?

If I do this am I better off implementing a queueing system within the worker? or is it not necessary?

I understand that this single worker is only a single thread and therefore the javascript operations will indeed occur synchronously within the webworker itself, but I am concerned about contention in a similar way that performing 200 ajax requests at once would overwhelm a browser.

I hope this makes.


Solution

  • The worker will queue up messages (specifically, it will queue up calls to onmessage) and process each message when ever the worker's call stack is empty.

    This means, however, that asynchronous operation could result in input multiple messages being processed before any any particular operation is completed. For example:

    onmessage = function(e) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "/" + e.data);
        xhr.onload = function() {
            postMessage(xhr.responseText);
        }
    }
    

    If you passed in 1000 messages at once, I suspect the browser will fire off 1000 Ajax requests (or at least a lot of requests) before it runs postMessage inside of onload. This is because onload does not run until the Ajax call is complete, and each onload call will be added to the event queue behind any pending calls to onmessage that were requested before the Ajax fetch could complete.

    If you wanted to ensure that only one Ajax request is out at a time, you could indeed implement an internal queue that moved only when onload was called:

    var queue = [];
    var busy = false;
    
    onmessage = function(e) {
        if(busy) {
            queue.push(e);
        }
        else {
            busy = true;
            runAjax(e);
        }
    }
    
    var runAjax = function(e) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", e.data);
        xhr.onload = function() {
            postMessage(xhr.responseText);
            if(queue.length) {
                // run the next queued item
                runAjax(queue.shift());
            } else {
                busy = false;
            }
        }
    }
    

    In this case, the first messages goes into the else block, which sets busy = true and runs the Ajax fetch. The following 999 messages are routed to the if block, because busy is true (and will not become false until the Ajax request completed and queue is empty). The queued messages are handled only after an Ajax request is complete.

    If you wanted to tolerate more than one Ajax request at the time, you could use a counter instead of boolean: use busy++ and busy-- instead of setting true and false, and test that the current number of outstanding connections has not reached your allowed max (i.e., if(busy == 5) instead of if(busy)).