dagda1
dagda1

Reputation: 28910

Multiple Web Worker ajax requests and not all returning

I have the following code in a web worker:

self.addEventListener('message', function(e){ 
        try {
      var xhr=new XMLHttpRequest()

      for(var i = 0; i < e.data.urls.length;i++){
        xhr.open('GET', e.data.urls[i], true);
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.send(null);
        xhr.onreadystatechange = function() {
                if (xhr.readyState == 4) {
                  if (xhr.status == 200 || xhr.status == 304 || xhr.status ==0) {
                    postMessage(xhr.responseText);
                  } else {
                    postMessage(xhr.status + xhr.responseText);
                    throw  xhr.status + xhr.responseText;
                  }
                }
        };
      } 
    } catch (e) {
     postMessage("ERROR:"+e.message);       
   }
}, false);

e.data.urls contains 16 requests which are handled on the UI thread like this:

var replies = 0;

worker.addEventListener('message', function(e){
    replies += 1;
});

Only 10 requests complete, is this because the UI thread has stopped before all the requests have returned or is there something else I am missing?

Upvotes: 4

Views: 1880

Answers (2)

pd40
pd40

Reputation: 3257

Your example is similar to this firefox example except for the loop inside the worker making multiple ajax requests. I'd be curious to know what is causing the failure. You might be hitting a limit on the number of concurrent ajax connections a single worker can process.

Could you try moving the url for loop to the main gui thread:

for(var i = 0; i < urls.length; i++){
    worker.postMessage(urls[i]);
}

and change your worker to just do a single ajax call at a time?

self.addEventListener('message', function(e){ 
    try {
        var xhr=new XMLHttpRequest()

        xhr.open('GET', e.data, true);
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.send(null);
        xhr.onreadystatechange = function() {
                if (xhr.readyState == 4) {
                  if (xhr.status == 200 || xhr.status == 304 || xhr.status ==0) {
                    postMessage(xhr.responseText);
                  } else {
                    postMessage(xhr.status + xhr.responseText);
                    throw  xhr.status + xhr.responseText;
                  }
                }
        };
    } catch (e) {
      postMessage("ERROR:"+e.message);       
    }
}, false);

and see if that works?


The mozilla example has some error handlers that might reveal the problem. You might try adding in the main GUI thread:

worker.onerror = function(error) {  
  dump("Worker error: " + error.message + "\n");  
  throw error;  
};  

and inside the worker:

function errorReceiver(event) {  
   throw event.data;  
}  

Upvotes: 0

marekful
marekful

Reputation: 15361

What is happening here is that your xhr variable gets overwritten in the loop. Due to the nature of XMLHttpRequest, that is, it is asynchronous by default, after the xhr.send(); line execution doesn't wait so for enters the next loop and the xhr.[...] lines operate on the xhr object set up and fired in the previous loop. Depending on whether the previous loop's request has returned (and thus the state change handler executed) or not (which is quite unpredictible) you overwrite either a 'live' or a 'finshed' xhr object. Those that get overwritten before they are finished are lost.

You should make sure you do not overwrite. Do not operate on the same XMLHttpRequest object but instantiate a new for each request.

I moved the definition of the handler function outside the loop. There's no need to redefine it in each loop. It is called in the context of the XMLHttpRequest instance it is assigned to so this points to the instance.

Also, I swapped the xhr.send() and the xhr.onreadystatechange = ... lines. A state change event handler should be assigned before the request is sent (there are several events fired right from the starting of the send) and althought unlikely, a request may even return with a ready state 4 before the line that adds the event handler is executed in the code.

self.addEventListener('message', function(e){ 

    var xhrs = [];

    function handler() {
      if (this.readyState == 4) {
        if (this.status == 200 || this.status == 304 || this.status ==0) {
          postMessage(this.responseText);
        } else {
          postMessage(this.status + this.responseText);
          throw  this.status + this.responseText;
        }
      }
    };

    for(var i = 0; i < e.data.urls.length;i++) {
      xhrs[i] = new XMLHttpRequest();
      xhrs[i].open('GET', e.data.urls[i], true);
      xhrs[i].setRequestHeader('Accept', 'application/json');
      xhrs[i].onreadystatechange = handler;
      xhrs[i].send(null);
    } 

}, false);

Upvotes: 3

Related Questions