OlivierBlanvillain
OlivierBlanvillain

Reputation: 7768

AJAX fire-and-forget, looking for the opposite of server-sent event

Is these an API symmetric to Server-Sent Event to generate fire-and-forget events from browser to server? I know how to not reply to a request on the server side, but how to tell the browser that it does not need to wait for a reply?

The goal here is to save resources on the client side, say you want to send 10k events to the server as fast as possible, not caring about what the sever replies.

Edit: While mostly irrelevant to the question, here is some background about the project I'm working on which would make use of an "AJAX fire-and-forget". I want to build a JavaScript networking library for Scala.js that will have as one of its applications to be the transport layer between Akka actors on the JVM and on a browser (compiled with Scala.js). When WebSockets are not available I want to have some sort of fallback, and having a pending connection for the duration of a round trip on each JS->JVM message is not acceptable.

Upvotes: 6

Views: 3904

Answers (6)

Craig Tullis
Craig Tullis

Reputation: 10497

So, just to be clear, you're trying to use the XMLHttpRequest as a proxy for your network communication, which means you are 100% at the mercy of whatever XMLHttpRequest offers you, right?

My take is that if you're going to stick with XMLHttpRequest for this, you're going to have to just make peace with getting a server response. Just make the call asynchronously and have the response handled by a no-op function. Consider what somebody else suggested, using a queue on the server (or an asynchronous method on the server) so you return immediately to the client. Otherwise, I really think JavaScript is just the wrong tool for the job you're describing.

XMLHttpRequest is going to be a different implementation (presenting a more or less common interface contract) in every browser. I mean, Microsoft invented the thing, then the other browser makers emulated it, then voila, everybody started calling it Web 2.0. Point being, if you push too hard at the doughy center of XMLHttpRequest, you may get different behavior in different browsers.

XMLHttpRequest, as far as I know, strictly uses TCP (no UDP option), so at the very least your client is going to receive a TCP ACK from the server. There is no way to tell the server not to respond at that level. It's baked into the TCP/IP network stack.

Additionally, the communication uses the HTTP protocol, so the server will respond with HTTP headers... right? I mean, that is simply the way the protocol is defined. Telling HTTP to be something different is kind of like telling a cat to bark like a chicken.

Even if you could cancel the request on the client side by calling abort() on XMLHttpRequest, you're not cancelling it on the server side. To do so, even if it were possible with XMLHttpRequest, would require an additional request sent all the way to the server to tell it to cancel the response to the preceding request. How does it know which response to cancel? You'd have to manage request id's of some kind. You would have to be resilient to out-of-order cancellation requests. Complicated.

So here's a thought (I'm just thinking out loud): Microsoft's XMLHttpRequest was based at least in spirit on an even earlier Microsoft technology from the Visual Interdev days, which used a Java applet on the client to asynchronously fire off a request to the server, then it would pass control to your preferred JavaScript callback function when the response showed up, etc. Pretty familiar.

That Java async request thing got skewered during the whole Sun vs. Microsoft lawsuit fiasco. I heard rumors that a certain original Microsoft CEO would blow a gasket any time he learned about Microsoft tech being implemented using Java, and kill the tech. Who knows? I was unhappy when that capability disappeared for a couple of years, then happy again when XMLHttpRequest eventually showed up.

Maybe you see where I'm going, here... :-)

I think perhaps you're trying to squeeze behavior out of XMLHttpRequest that it just isn't built for.

The answer might be to just write your own Java applet, do some socket programming and have it do the kind communications you want to see from it. But then, of course, you'll have issues with people not having Java enabled in their browsers, exacerbated by all the recent Java security problems. So you're looking at code-signing certificates and so on. And you're also looking at issues that you'll need to resolve on the server side. If you still use HTTP and work through your web server, the web server will still want to send HTTP responses, which will still tie up resources on the server. You could make those actions on the server asynchronous so that TCP sockets don't stay tied up longer than necessary, but you're still tying up resources on the server side.

Upvotes: 5

Dharmang
Dharmang

Reputation: 3028

As you have asked for "how to tell the browser that it does not need to wait for a reply?" I assume that you do not want to process the server reply.

in such case, it is better to utilize one pixel image response trick which is implemented by Google for analytics and tracking, and many other such services.

More details here

The trick is to create new image using javascript and set src property, the browser will immediately fire the request for image and browser can parallelly request form multiple such requests.

var image = new Image();
image.src = "your-script.php?id=123&other_params=also";

PROs: easy to implement less load on server/client, then ajax request

CONs: you can send only GET requests using this appproach.

Edit

For more references:

http://help.yahoo.com/l/us/yahoo/ywa/faqs/tracking/advtrack/3520294.html

https://support.google.com/dfp_premium/answer/1347585?hl=en

How to create and implement a pixel tracking code

Again they are using same technique of pixel image.

Upvotes: 5

Nelson Menezes
Nelson Menezes

Reputation: 2094

You won't have the same level of fine-grained control of the connection with XHR as with WebSockets. Ultimately, it's the browser that manages the HTTP connection lifecycle.

Instead of falling back from WebSockets to discrete XHR connections, maybe you can store and batch your events. For instance:

Client JS

function sendMessage(message) {
    WebSocketsAvailable ? sendWithWebSockets(message) : sendWithXHR(message);
}

var xhrQueue = [];

function sendWithXHR(message) {
    xhrQueue.push({
        timestamp: Date.now(),  // if this matters
        message: message
    });
}

function flushXhrQueue() {
    if (xhrQueue.length) {
        var req = new XMLHttpRequest();
        req.open('POST', 'http://...');
        req.onload = function() { setTimeout(flushXhrQueue, 5000); };
        // todo: needs to handle errors, too
        req.send(JSON.stringify(xhrQueue));
        xhrQueue = [];
    }
    else {
        setTimeout(flushXhrQueue, 5000);
    }
}

setTimeout(flushXhrQueue, 5000);

On the server, maybe you can have two endpoints: one for WebSockets and one for XHR. The XHR handler deserialises the JSON queue object and calls (once per message) the same handler used by the WebSockets handler.

Server pseudo-code

function WSHandler(message) {
    handleMessage(message, Date.now());
}

function XHRHandler(jsonString) {
    var messages = JSON.parse(jsonString);

    for (var messageObj in messages) {
        handleMessage(messageObj.message, messageObj.timestamp);
    }
}

function handleMessage(message, timestamp) {
    ...
}

Upvotes: 0

OlivierBlanvillain
OlivierBlanvillain

Reputation: 7768

I managed to get the expected behavior using a very small timeout of 2ms. The following call is visible by the server but the connection is closed on the client side before any reply from the server:

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
  if (xhr.readyState == 2) {
    alert("Response header recived, it was not a fire-and-forget...");
  }
};
xhr.open("POST", "http://www.service.org/myService.svc/Method", true);
xhr.timeout = 2;
xhr.send(null);

This is not fully satisfactory because the timeout may change between browser/computers (for instance, 1ms does not work on my setup). Using a large timeout in the order of 50ms means that the client might hit the limit of maximum concurrent opened connections (6 on my setup).

Upvotes: 1

ilivewithian
ilivewithian

Reputation: 19702

It sounds like you're trying to solve the wrong problem. Instead of dealing with this on the client, why not handle this on the server side.

Take the message from the client and put a message on a service bus or store the data in a database and return to the client. Depending on your stack and architecture, this should be fairly simple and very fast. You can process the message out of band, either a second service listens to the message bus and processes the request, or some sort of batch processor can come along later and process the records in the database.

Upvotes: 0

broofa
broofa

Reputation: 38132

Using XMLHttpRequest to send an async request (i.e. where you don't care if it succeeds or what the response is:

var req = new XMLHttpRequest();
req.open('GET', 'http://my.url.goes.here.com');
req.send();

You can do much the same thing with an Image object, too, btw:

new Image().src = 'http://my.url.goes.here.com';

The Image approach works particularly well if you're making cross-domain requests, since Images aren't subject to same-origin security restrictions the way XHR requests are. (BTW, it's good practice but not essential to have your endpoint return a 1x1 pixel PNG or GIF response with the appropriate Content-Type, to avoid browser console warnings like 'Resource interpreted as Image but transferred with MIME type text/html'.)

Upvotes: 0

Related Questions