views:

679

answers:

4

I am trying to send some content to the client before doing some lengthy work:

Response.Write("Processing...");
Response.Flush();
System.Threading.Thread.Sleep(5000);
Response.Write("Finish");
Response.End();

In Firefox it works as expected but in IE8, Safari and Chrome it waits until all the code is processed and then shows the whole text.

I have tried sending a better formed HTML like the below sample but I get the same results:

Response.Write("<html><head><title>test</title></head><body>Processing...</body></html>");
Response.Flush();
System.Threading.Thread.Sleep(5000);
Response.Write("Finish");
Response.End();

Thank you!

+1  A: 

The problem you are facing is that the response you are sending is still incomplete. Even though you flush whatever is in the buffers to the browser, it is still up to the browser to wait for the response end or process what it's got so far - hence the difference between browsers.

What's even worse is that you can expect the same behavior from some intermediate nodes concentrators, firewalls, etc. located on the internet between your server and the browser.

The bottom line is that if you want to ensure that browser does something with your data stream you have to complete it with Response.End.

In other words if you want to send some of your response data first and delay sending the rest your better option is to break the response in two, complete the first one and download the second part separately

mfeingold
So AJAX is the only way to do it?
Dawkins
You can play some other tricks - IFrame is one, but ajax is the most strightforward one and with frameworks like JQuery it should not be a challenge to implement
mfeingold
+1  A: 

An easy way to resolve this is to place a "Please wait, processing" page in front of the actual page that does the work. The "please wait" message is displayed and then it immediately starts processing by using a meta refresh tag and/or javascript to redirect to the actual processing page.

"Please Wait" Page:

<html>
<head>
  <meta http-equiv="refresh" content="0;url=process.aspx?option1=value&...." />
  <title>Please Wait, Processing</title>
</head>
<body>
  Processing...
</body>
<script type="text/javascript">
  window.location = "process.aspx?option1=value&....";
</script>
</html>

Notes:

  1. Using two methods to kick off the processing is done to ensure if a browser can't use one, it will hopefully use the other method.
  2. You will have to replace the processing url and querystring to suit.
  3. One downside of this method is that if the user hits the browser back button, they will be going back to the "please wait" page from the "process" page, meaning it will accidentally kick off the job again. I'll leave that challenge for another topic!
Will
Excellent! easy and simple... I just upvoted your answer because the question was why Flush() was not working, but your answer helped me a lot. Thanks!
Dawkins
Thanks =)As mfeingold says, the response is being flushed, the problem lies with the browsers, as they decide when they will render what has been received so far.Hence my idea, which ensures the whole Please Wait page is received and therefore rendered before processing starts.The joys of web programming!
Will
+1  A: 

When you call Response.Flush() before the response is complete (before Content-Length is known), the ASP.NET runtime generates a chunked-encoding partial response. It's up to the browser to decide how to render that. In my testing I've found that browsers do tend to render images (<img> tags) that are included in a partial response. You might give that a try.

However, be careful about sending </html> too soon; browsers may ignore anything past that point. Browsers do render partial pages all the time, though -- so you can start with the beginning of your page as usual.

In case it's helpful, I walk through an example of this in detail in my book, including a packet trace that shows exactly what happens on the wire: Ultra-Fast ASP.NET.

RickNZ
+1  A: 

Also, be aware that if your IIS server is compressing the output with GZIP, then it will seem to ignore all Response.Flush calls.

This is turned on by default in IIS7 and on Windows 7.

And, if you are testing with Fiddler, be sure to turn on "Streaming" mode, or Fiddler will collect the flushed HTML and hold it until the connection is completed.

Glen Little