Reputation: 28910
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
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
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