Ortund
Ortund

Reputation: 8255

Progress indicator for long running tasks in MVC 5

Preface: I've gone ahead and used this as the basis for what I'm doing.

I'm trying to give my user some indication that submitting the form is actually doing something.

My idea was something very similar to what YouTube has done... Have a bar across the top of the page expand in width across the page to mirror the completion progress of the task being performed.

Here's the jquery that submits the form:

    // Task Progress Indication

    function update(taskId, status) {
        var e = $("#" + taskId);
        if (status != "Completed") {
            // increase the width of the progress indicator
            e.css("width", status);
        }
        else {
            e.hide();
        }
    }

    $("form").submit(function (e) {
        // start indicating progress
        e.preventDefault();
        $.post("Home/Start", {}, function (taskId) {

            // periodically update monitor
            var intervalId = setInterval(function () {
                $.post("Home/Progress", { id: taskId }, function (progress) {
                    if (progress >= 100) {
                        update(taskId, "Completed");
                        clearInterval(intervalId);
                    }
                    else {
                        update(taskId, progress + "%");
                    }
                });
            }, 100);
        });
        // end indicating progress

        // this is the post of the form to the Controller
        $.post($(this).attr("action"), $(this).serialize(), function (data) {
            if (!data.IsOK) { // this is some error handling that I need to fix still
                $("#modalTitle").html(data.Title);
                $("#modalMessage").html(data.Message);
                $("#modalDetail").html(data.Error).hide();
                $("#modalDialog").css("display", "block");
                $("#modalBackground").css("display", "block");
            }
            else {
                window.location.href = '@Url.Content("~/")';
            }
            return;
        });

        return false;
    });

On my controller I have the following ActionResult which are to handle the updating of the progress indicator.

private static IDictionary<Guid, int> tasks = new Dictionary<Guid, int>();

public ActionResult Start()
{
    var taskid = Guid.NewGuid();
    tasks.Add(taskid, 0);

    Task.Factory.StartNew(() =>
    {
        for (var i = 0; i <= 100; i++)
        {
            tasks[taskid] = i; // update task progress
            Thread.Sleep(50); // simulate long running operation
        }
        tasks.Remove(taskid);
    });
    return Json(taskid);
}

public ActionResult Progress(Guid id)
{
    return Json(tasks.Keys.Contains(id) ? tasks[id] : 100);
}

I may be dead wrong here but I think the reason I'm seeing nothing on the page is that there's no link here between the progress indication and the form submission.

How do I fix this by linking the form submission to the progress indication?

Upvotes: 0

Views: 2345

Answers (1)

Ross Bush
Ross Bush

Reputation: 15185

I implemented similar functionality to provide feedback while merging multiple ssrs pdf outputs into one file. I had to provide some feedback to the user during the process. It was a pain but I learned that the way .NET likes to serialize access to sessions interferes with the way the callbacks are returned. It is possible that you are running into the same issue.

My soultion to the problem, pointed out by John Saunders in this question, was to simply turned off session state for the controller method of the client progress postback.

//IMPORTANT - NORMAL PROGRESS STATE SERIALIZES ACCESS TO SESSIONS - All callbacks are returned only after the controller method returns
[SessionState(SessionStateBehavior.ReadOnly)]
public class MyProgressCallbackClass
{
    ...
}

Upvotes: 1

Related Questions