同事要用網頁跑一段很久的程序,因此想用"簡便"的方法產生即時狀態更新的效果,以免使用者等到抓狂。(簡便->代表用最少的Code做出需要的功能,有沒有用到又酷又炫的AJAX,架構、程式漂亮與否是其次)

我想到最簡單的方法是在這段要執行很久的ASP.NET中,用Response.BufferOutput = false加上Response.Write("..")、Response.Flush(),每執行到一個段落,輸出一部分內容來更新狀態。

於是我寫了以下的Sample Code:

<%@ Page Language="C#" %>
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        Response.BufferOutput = false;
        Response.Flush();
        for (int i = 1; i < 10; i++)
        {
            Response.Write("<li>" + i.ToString());
            Response.Flush();
            System.Threading.Thread.Sleep(1000);
        }
        Response.Write("</body></html>");
        Response.End();
    }
</script>
<html">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    </form>
</body>
</html>

在我的機器上測試成功後,丟給同事測試,傳來結果卻是最後才一次輸出所有數字。

自己將程式放到不同機器反覆測試了一下,發現有趣的現象: 這段程式有時可以陸續印出1, 2, 3, 4、有時則會等到最後才一次印出。而且往往第一次執行不OK,但按下IE的重新載入卻又成功。

會不會問題出在瀏覽器端?

於是我用HttpWatch進行觀察,驗證了我的猜測,在成功與失敗兩種情況中,資料都有陸續送至瀏覽器,但卻分別得到陸續印出或一次印出兩種結果。

我又另外試了Firefox及Chrome,得到更有趣的結果: Firefox永遠陸續印出,Chrome則永遠一次印出。

我大膽推論,當收到的HTML碼還不完整時,要不要Render出元素出來,取決於Browser。Firefox選擇了收到立即處理,Chrome則是等收齊再說。而IE最彈性,當發現接收資料的速率較慢時,怕會收到太多破碎不完整的HTML片段做白工,選擇收集齊再產出;當接收速度不差時,就採行收到立即處理。這個推論可以解釋,為何在本機總是成功,在遠端第一次失敗,Reload會成功,但是否為真我就不敢確定了。

當HTML不完整時,各瀏覽器的處理原則不一,看來這個方法並不理想。我想到另一招,<script>中的段落應該都會即時處理才對,所以將程式修改如下:

<%@ Page Language="C#" %>
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        Response.BufferOutput = false;
        Response.Clear();
        Response.Write("<html><body><span id='spnDisp'></span>");
        Response.Flush();
        for (int i = 1; i < 10; i++)
        {
            Response.Write("<script type='text/javascript'>");
            Response.Write("document.getElementById('spnDisp').innerHTML='" 
+ i.ToString() + "';");
            Response.Write("</" + "script>");
            Response.Flush();
            System.Threading.Thread.Sleep(1000);
        }
        Response.Write("</body></html>");
        Response.End();
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    </form>
</body>
</html>

Bingo!! 使用IE, Firefox, Chrome測試都可成功運作! 不過我還是不確定,會不會在某些特例下失常? 大家看看還有沒有什麼點子,歡迎討論。


Comments

# by 讚啊!

不錯,很好的東西!

# by Will 保哥

這一行有讓我會心一笑: Response.Write("</" + "script>"); P.S. 這是 ASP.NET 的 Behind 喔! ^_^

# by Jeffrey

to Will, 經驗裡放在Inline Code時會有這個問題,如果寫在Code Behind的cs中就沒這個顧忌。巧得很,才讀到一篇相關文章沒幾小時(http://www.dotblogs.com.tw/yilinliu/archive/2008/08/14/4909.aspx ),馬上就給我來個臨時抽考。

# by Johnny

如果使用 Response.Write 輸出 JavaScript 的話, 如果網頁中有使用 UpdatePanel, 我記得是會發生問題。

# by Martin

請問一下, 這個效果在UpdatePanel中要怎麼樣才能做到呢?

# by Jeffrey

to Martin, 如果是UpdatePanel,應該做法會很不相同,我的想法是分成兩個網頁,一個用來跑程式,一個則用來看進度。跑程式時要定期將狀態更新在Server端的Session或Cache,進度程式就可以用定期觸發UpdatePanel的方式讀到最近的進度。大概的會從這個方向去研究,但似乎稍有難度就是了。

# by 貓霸

真是難以為情,今天才發生RESPONSE.BINARYWRITE的狀況,因為過年前換了台全SSD網頁+MSSQL伺服器,一個輸出EXCEL的頁面出了問題,上估狗搜到這裡,加了一句Thread.Sleep(1000);,搞定! 看來伺服器跑太快也會有問題,尷尬

Post a comment