7

Imagine as given the following situation:

There are about 20 requests (or even more) which has been triggered by our website. These can be any kind of requests - we don't know how to trigger them again. On this website, all the requests target the same url. The requests can have subscribed event listeners.

In case of using Chrome, the first 6 requests are sent and the others are waiting in a queue to be sent (because of the parallel request limit per domain).

At this moment the webpage triggers a very important request (lets call it "VIR") which has a higher priority to be sent to the server then the previous 20 requests. The other requests (and their event listeners) are also kind of important so we can't just abort them to send the VIR immediately.

We need a solution to get all the pending requests (6 sent + 14 in the queue), abort them, then send the VIR, and then send the others again with the same event listeners attached that they had before.

In case of no other (out of the box) solution, the 2 basic questions are:

  • Is it possible to get reference to all the pending requests (including the queue)?
  • Is it possible to abort an xhr and then send it again (by cloning them somehow, or I don't know)?

And another related question:

  • I remember that the parallel requests limit per Hostname is a limit for the browser and not for the current tab only. Is there a magical solution to handle it? If yes, do I really want to write that? :)

Be aware of that all the requests have to be sent the same domain. (Means that targeting a different domain with the VIR is not an option here). However using websocket or http/2 could solve the basic problem, those are not options in this current question.

I appreciate any idea on this! Thx in advance!

pm.: And yes, it's a javascript question :)

Burnee
  • 2,453
  • 1
  • 24
  • 28
  • 7
    Your actual problem is 20 concurrent ajax requests targeting the same url. Fix that. – Bergi Aug 22 '16 at 18:25
  • 1
    You could rewrite `XMLHttpRequest.prototype.send` to add requests to a list as they're sent, and within that overwriting function also add a completion listener to remove it from the list, similar to http://stackoverflow.com/a/18259603/. Once you've done that, though, re-sending a request can't be done; you'd need to build a new one. In order to that, you have to capture all the information about the request as it's being constructed, which means also overwriting `open`, `setRequestHeader`, etc. (I think Bergi really has the right idea, though `:)` ) – apsillers Aug 22 '16 at 18:28
  • Yes, Bergi could be right, but this question is for itself, to see how can you deal with this kind of situation. And I expected the answer that you've given. Although I'm asking for more details about properly cloning an xhr object. Should I just overwrite all the properties of the new xhr with the old one's own properties? Or should I exclude some? Does this "copies" the xhr's event handlers too? – Burnee Aug 22 '16 at 21:35
  • You're right; using websocket or http/2 could solve a similar problem. But those are not options in this current question – Burnee Aug 22 '16 at 23:09
  • You have a [bigger problem](http://stackoverflow.com/a/30064610/248058). – Knu Aug 24 '16 at 03:00
  • I've read this post before posting my question (Actually I've read the whole internet :D ). But thx anyway, it's still good to have it here for other visitors. – Burnee Aug 24 '16 at 12:08

1 Answers1

4

You can actually keep using the same XmlHttpRequest instance after aborting the request, calling the open and send methods again, and the event listeners remain attached. Updated the snippet with overloading the xhr open/send method to save the url and payload:

var pending_requests = [], 
    XHRBase = {
      open: XMLHttpRequest.prototype.open, 
      send: XMLHttpRequest.prototype.send
    };

XMLHttpRequest.prototype.open = function() {

  pending_requests.push(xhr._data = {xhr: this, open: arguments});
  XHRBase.open.apply(this, arguments);
  // add event listener to pop on finish
}

XMLHttpRequest.prototype.send = function() {
  xhr._data.send = arguments;
  XHRBase.send.apply(this, arguments);
}

function priority_call(params, callback) {
  pending_requests.forEach((req) => req.xhr.abort());
  // do vip xhr
  pending_requests.forEach((req) => {
    XHRBase.open.apply(req.xhr, req.open); 
    XHRBase.send.apply(req.xhr, req.send);
  })
}
yscik
  • 879
  • 4
  • 7
  • In case we could control all the requests, this could be a good solution. However the case is that we can't control them. Overriding the XmlHttpRequest could be a solution to collect all the AJAX calls. – Burnee Aug 24 '16 at 12:04
  • 1
    Turns out you can re-use the xhr instance, updated the answer with that solution. – yscik Aug 24 '16 at 16:12
  • Big UP dude! Thx for your effort, now it's pretty close to the thing that I'v imagined. I'll work on this on the following week, testing it from A to Z. I'll come back to share the experiences. Based on the results I'll accept the answer then. – Burnee Aug 24 '16 at 21:31