8

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>
Craig
  • 34,658
  • 33
  • 108
  • 191

2 Answers2

15

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"

Community
  • 1
  • 1
Pablo Romeo
  • 10,870
  • 1
  • 27
  • 57
  • Yes, I already do something like that but if it is the same message for 10 seconds people think the system has hung. – Craig Jul 05 '12 at 04:45
  • 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. – Pablo Romeo Jul 05 '12 at 05:32
5

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>
Ryan Kohn
  • 11,921
  • 10
  • 50
  • 80
Jeroen K
  • 9,598
  • 5
  • 37
  • 38