9

I am just using the new Async Controller features in MVC 4 as described here http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

If I have an action that may take 10-20 seconds to run I would like to provide some kind of status bar to notify the user of progress. Do the Async features have anything to help this out?

EDIT: I will take a stab at how I will try and do it and see if there are any better ways

public async Task<ActionResult> GizmosAsync()
{
    return View("Gizmos", await GetGizmosAsync());
}

private void GetGizmosAsync()
{
    for(int i=0; i<10; i++) 
    {
        lock(_locker) 
        {
           _statusMessage = String.Format("{0} of 10", i);
        }  
        DoSomethingLongRunning();
    }
}

public ActionResult Status()
{
   return Json(new { Status = _statusMessage });
}

static readonly object _locker = new object();

static string _statusMessage = "";

....

<script>

setTimeout(displayStatus, 1000);

function displayStatus() {
  $.post("/controller/status", function(data) {
    alert(data.Status);
  });
}

</script>

2 Answers 2

17

Async controllers is just a mechanism for freeing threads from the ThreadPool in IIS in order to be able to handle incoming requests during heavy load, but the communication with the client remains as the usual request-response.

Status bars and the sort are usually just javascript displaying something on screen until the ajax request finishes. I don't think MVC4 will be of aid in that part.

You could do something like this: https://stackoverflow.com/a/68503/1373170 to display a "processing..." <div> during ajax calls.

EDIT: If you need real client progress and interaction (such as real progress), you should check out SignalR http://www.hanselman.com/blog/AsynchronousScalableWebApplicationsWithRealtimePersistentLongrunningConnectionsWithSignalR.aspx And this related post: Async Controllers (MVC), long running process with "stops"

Sign up to request clarification or add additional context in comments.

2 Comments

Yes, I already do something like that but if it is the same message for 10 seconds people think the system has hung.
Well, I know it may not sound appropriate, but sometimes the "illusion" of progress is just what the users need. For example, if you know your task takes on average 15sec, automate a progress bar through that duration using js. I know it may seem like its fooling the user, but there really are some cases where actually storing partial progress somewhere, like a database and have your client-side polling for exact progress is just overkill.
6

This article seems to describe what you want:

ASP.NET MVC 3: Async jQuery progress indicator for long running tasks

Controller:

public class HomeController : Controller
{
    private static IDictionary<Guid, int> tasks = new Dictionary<Guid, int>();

    public ActionResult Index()
    {
        return View();
    }

    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);
    }
}

View:

<script type="text/javascript">

function updateMonitor(taskId, status) {
  $("#" + taskId).html("Task [" + taskId + "]: " + status);
}

$(function () {
  $("#start").click(function (e) {
   e.preventDefault();
   $.post("Home/Start", {}, function (taskId) {

     // Init monitors
     $("#monitors").append($("<p id='" + taskId + "'/>"));
     updateMonitor(taskId, "Started");

     // Periodically update monitors
     var intervalId = setInterval(function () {
       $.post("Home/Progress", { id: taskId }, function (progress) {
         if (progress >= 100) {
           updateMonitor(taskId, "Completed");
         clearInterval(intervalId);
         } else {
           updateMonitor(taskId, progress + "%");
         }
        });
      }, 100);
    });
  });
});
</script> 
<div id="monitors"></div>

2 Comments

Not good if you are using a load balanced multiple AppDomain environment.
I know it has been a while, say if using DbContext using DI which usually can be the case for loading data to a table, the DbContext is lost as soon as the response is sent, then the only way would be to manually get DbContext from the container and load the data ...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.