Eric Sassaman
Eric Sassaman

Reputation: 1471

ASP.net progress bar updated via jQuery ajax during async operation

I'm trying to set up a simple html progress bar for a long-running async task. The problem is that I'm trying to fire off a postback to run some server code and do a client click event at the same time to start the ajax polling, which is causing an error return in the ajax call. Here's my jQuery and html code:

<%@ Page Language="C#" AsyncTimeout="230" CodeFile="test.aspx.cs" Inherits="test" %>

<head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
    <script>
        "use strict";
        var PROGRESS;
        $(document).ready(function () {
            $.ajaxSetup({
                type: "POST",
                contentType: "application/json",
                data: '{}',
                timeout: 10000,
                converters: {
                    "text json": function (data) {
                        var msg;
                        msg = $.parseJSON(data);
                        return msg.hasOwnProperty('d') ? msg.d : msg;
                    }
                }
            });
            PROGRESS = {
                done: false,
                startProgress: function () {
                    var _progress = this;
                    $.ajax({
                        url: 'test.aspx/getProgress',
                        timeout: 20000,
                        data: '{}',
                        dataType: "json",
                        success: function (data) {
                            var currProgress = data * 100;
                            $("#prog").value = currProgress;
                            if (currProgress > 99)
                                _progress.done = true;
                            else
                                setTimeout("PROGRESS.startProgress()", 5000);
                        },
                        error: function (jqXHR, textStatus, errorThrown) {
                            var s = textStatus == null ? "" : textStatus;
                            s += errorThrown == null ? "" : ", " + errorThrown;
                            alert(s);
                        }
                    });
                }
            };
            $("#body_DoUploadButton").on("click", function (event) {
                //event.preventDefault();
                PROGRESS.startProgress();
            });
        });
    </script>
</head>
<body>
    <asp:Button ID="DoUploadButton" Text="Import file" OnClick="UploadButton_Click" runat="server"></asp:Button>
    <progress id="prog" value="0" max="100"></progress>
</body>

Here is my codebehind:

public partial class test : System.Web.UI.Page
{
    delegate void AsyncTaskDelegate();
    AsyncTaskDelegate _dlgt;

    IAsyncResult OnBegin(object sender, EventArgs e, AsyncCallback cb, object extraData)
    {
        _dlgt = new AsyncTaskDelegate(DoImport);
        IAsyncResult result = _dlgt.BeginInvoke(cb, extraData);
        return result;
    }

    void OnEnd(IAsyncResult ar)
    {
        _dlgt.EndInvoke(ar);
    }

    void OnTimeout(IAsyncResult ar)
    {
        //do something
    }

    [WebMethod(EnableSession = true)]
    public static double getProgress()
    {
        if (HttpContext.Current.Session["pctDone"] == null)
            return 0.0;
        return (double)HttpContext.Current.Session["pctDone"];
    }

    protected void DoImport()
    {
        int i = 10;
        while (i-- > 0)
        {
            System.Threading.Thread.Sleep(5000); //fake some work
            Session["pctDone"] = i / 10.0;
        }
        Session["pctDone"] = 1.0;
    }

    protected void UploadButton_Click(object sender, EventArgs e)
    {
        Session["pctDone"] = 0.0;
        PageAsyncTask task = new PageAsyncTask(new BeginEventHandler(OnBegin), new EndEventHandler(OnEnd),
            new EndEventHandler(OnTimeout), null);
        RegisterAsyncTask(task);
        ExecuteRegisteredAsyncTasks();
    }

    protected void Page_Init(object sender, EventArgs e)
    {
        Server.ScriptTimeout = 240;
    }
}

Apparnetly the postback when clicking the button is interfering with the ajax call. If I add a preventDefault in the javascript clickhandler, the postback never fires (duh) but all the ajax calls work, so at least all the ajax code is working ok. Is there a way to have a button both postback, so my server code can run, and do a client click event so I can start the ajax calls? I think I'm screwed here, I need to do this all with no asp postbacks, since then the page is destroyed and reloaded, right?

I'm sure I'm going about this all wrong. I tried moving all this to a service thinking I'll avoid all postbacks and just make ajax calls to a service to do the async work and also poll the service for percent done, but services don't implement page async so there's no "RegisterAsyncTask()" to call like you can on an .aspx page.

I'm also trying to use the session state to communicate the value of the percent done between the async task and the ajax call, which I have no idea if it will work, but I thought I'd experiment. If it fails I need some way to communicate the percent between the background task and the ajax call.

I've read a lot on how to do make async webservices and using the Beginxxx calls etc. in the proxy generated by the webservice, but I also found a lot of posts of people trying to do something similar to me, making simple ajax calls via jQuery, and having no luck and no real answers given that I can find.

So from an .aspx page how do I start a long-running task either with page async or a service, and update a simple html5 progress bar as the task runs? Seems like that would be simple, but no :\

Upvotes: 4

Views: 6684

Answers (2)

Eric Sassaman
Eric Sassaman

Reputation: 1471

After much pain and suffering I have completely rewritten my solution to use SignalR, which vastly simplifies all the code required. For more details on the easy way to do a simple HTML5 progress bar for a long running task using SignalR in a Web Forms web site, see my blog post on the subject, which includes a downloadable minimalist sample and working online demo. I hope someone finds that helpful.

Upvotes: 0

nunespascal
nunespascal

Reputation: 17724

You are missing just one basic point. Your ajax calls will not survive a postback.

You cannot call PROGRESS.startProgress() on button click, cause you want the page to post back immediately after that. A postback would cause your ajax call to be terminated.

If you want to show a progress bar, you must upload the file too using ajax and not by using a postback. There are plugins like uploadify that can help.

Upvotes: 2

Related Questions