A while back I posted a possible solution to dealing with long running processes in a web application. While that solution works for very basic processes, the use of threading in an asp.net application can be the cause of a lot of grief (there are just too many ways outside of your control for those threads to be aborted prematurely).
I did a little research and came up with a MUCH better solution – simply execute the ajax request for the long running process, and then listen for messages on another ajax request. The key to this working in IIS/.NET, however is to ensure that your long running process is a SESSIONLESS request, otherwise your request will block further
ajax requests until it’s completed.
In my previous solution, I pointed out a number of weakneses with the simple polling technique I used to send status messaged back to the client. Polling is too chatty, especially if you wind up sending redundant status data back from the server regularly.
In this blog post, I’ll focus on the improvement to the message passing portion of the solution.
As web developers we are constantly on the lookout for server requests that take ‘too long’ to respond, so this solution may be a little counter-intuitive for some. The basic idea is that we submit an ajax request to the server which INTENTIONALLY doesn’t return a response to the client until it has new data to send back. The code on the server waits in a loop until the relevant data is available. When the client eventually receives the response, it IMMEDIATELY makes another request to the server for more data. In this way it will always have an open connection to the server, which can send data back to the client when IT is ready, not when the client decides it’s interested. This simulates a ‘push’ of data from the server to the client, which is an extremely useful concept.
In order to have a good user experience this design does need to incorporate a bit more error handling. First, the client must handle timeout errors (default timeouts can be set when you make the ajax request, or you can accept the server defaults (typically 20 minutes). Second, the client should have a way of aborting the ‘listening’ process based on user input. Third, the server should handle some type of logical timeout in case it unexpectedly stops finding data to send back to the client. The method used to communicate a server-initiated timeout could also be used to communicate other logical statuses, such as ‘process completed – stop listening’.
On the server
I’ll show the code for the server first, because this is actually the simplest part of the solution:
As you can see, this simply loops until it has something to send back to the client. I chose to sleep the loop for .5 seconds in order to avoid consuming too many resources on the server. You can also see that I implemented a simple 1 minute timeout.
Note: in a real-world production application, you should use asynchronous controller methods, or you will risk depleting the IIS threadpool. This is just a sample app focusing on the communication pattern. Another alternative would be to use a completely different messaging server that doesn’t consume threads as agressively as IIS/.net – such as Faye built on top of node.js. In this option, the .net server would send messages to the messaging server, and the web clients would ‘listen’ to the messaging server.
On the client
This is a simple ajax request, with handlers for errors and success. You’ll notice I return the jqXHR object recieved from the ajax call. This is used later to abort the open connection based on user input. The error handler isn’t that interesting – it just displays the error and stops the process.
When we recieve a message from the server, we display it and then check to see if we should continue listening for messages. In this case I have encapsulated the code into a ‘listener’ object which has start(), abort(), and isListening() methods, so I call the abort() method simply to reset the state of the object, not to abort the ajax request (which is completed at this point).
Here are their implementations:
And to wrap things up, here is how these are wired into the user interface:
You might notice that I have the UI code pass in a callback function which is responsible for displaying the recieved messages in the UI. This provides a nice clean separation of concerns between the message handling and the UI elements.
You can find a complete asp.net MVC3 solution here:Comet Demo Naturally this pattern could be implemented in any development platform, although an ajax library such as jQuery and an MVC framework that handles the details of dealing with json data keeps things a lot simpler.
Let me know if you have feedback on this pattern or on the code sample.