Reputation: 372
I have an AJAX intensive application that requires sending multiple AJAX requests rapidly or concurrently. The following code is just a simple wrapper for sending AJAX POST calls I use throughout the app. There are 2 caveats:
1) I want to be able to test the user's internet connection before making the request, so I can notify them if their connection is down.
2) If their connection is down and they continue to use the app, which generates more AJAX calls, I want to queue those calls and send them one by one once connectivity returns.
The connectivity check and queueing work, however when the user comes back online only some of their requests are sent to the server, and they seem to be sent out of their original order. What am I missing? Why aren't all the requests being sent, and why aren't they in order?
And before anyone notes, I have seen a few other solutions on this topic involving jQuery. I'm not opposed to using those, I just want to understand why THIS code isn't working. Thanks in advance.
window.connectionState = true
window.xhrQueue = []
window.pingInterval
function xhrPost(url, packet, before, after) {
if (!url || typeof(url) !== "string") {
console.log("invalid url supplied in xhr call.")
return false
}
var mainRequest = function() {
var xhr= new XMLHttpRequest()
if (typeof(after) === "function") {
xhr.onreadystatechange = function(){
if (xhr.readyState == 4) {
after(xhr)
return true
}
}
}
if (typeof(before) === "function") {
before()
}
xhr.open("POST",url,true)
if (packet) {
xhr.send(JSON.stringify(packet))
}
else {
xhr.send()
}
}
ping(mainRequest)
}
function ping(mainRequest) {
// Create pingXhr to test connection
var pingXhr = new XMLHttpRequest()
pingXhr.onreadystatechange = function(){
// If pingXhr comes back successfully...
if (pingXhr.readyState == 4) {
if (pingXhr.status == 200) {
// If pingXhr comes back from being down, update user
if (window.connectionState !== true) {
setTimeout(function() { alert("And we're back! Your connection seems to be working now. Keep editing.") }, 1)
}
// If there are requests waiting, send them in order, then remove them
if (window.xhrQueue.length > 0) {
for (var i in window.xhrQueue) {
ping(window.xhrQueue[i])
window.xhrQueue.splice(i, 1)
clearInterval(window.pingInterval)
}
}
// Otherwise, just make the singular request
else {
mainRequest()
}
// Reset xhrQueue since stuff is successful, change connection to true, and unset onbeforeunload message
window.xhrQueue = []
window.connectionState = true
}
// If there was a problem with the request
else {
// Notify the user their internet is down
if (window.connectionState === true) {
setTimeout(function() { alert("It seems you have momentarily lost internet connectivity.") }, 1)
}
// If there are no requests in the xhrQueue, create the timeout. Otherwise, just add to the queue
if (window.xhrQueue.length === 0) {
window.pingInterval = setInterval(function(){ ping() }, 3000)
}
// Add the request to the xhrQueue to be processed in order
if (typeof(mainRequest) === "function") {
window.xhrQueue.push(mainRequest)
}
window.connectionState = false
}
}
}
pingXhr.open("GET","/some/url/here",true)
pingXhr.send()
}
Upvotes: 1
Views: 1815
Reputation: 19782
It looks like you're using push() to place entries on the queue, then using splice() in a loop to remove them. That's not likely to work properly - it'll skip some/most of them, because the splice modifies the indexes in the array as you're iterating over them.
If you change your loop to always take the first element off, it'll work better.
edited to add: You probably don't want to do a for-in loop here, either. Modifying the keys of the object while you're iterating over it is not generally a good idea.
something like:
while (window.xhrQueue.length > 0) {
ping(window.xhrQueue[0]);
window.xhrQueue.splice(0, 1);
}
Or instead of trying to run all of the queued requests simultaneously, you could have the onreadystatechange handler grab the next entry off the queue and just send that request.
Upvotes: 1
Reputation: 2276
it will be because you are firing them all off at once, some will take longer to get back than others so their handlers will run first.
I suggest you send them one at a time using the callback to send the next one
Upvotes: 1