ASP.NET MVC 4 Async Controller Callback


Answers

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>
Question

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>



Do not use "threads" in an web app. Let the server handle this by using "async" calls. Otherwise you would have to setup a threadpool and que the slow request.




For jQuery I use

jQuery.ajaxSetup({
  beforeSend: function() {
     $('#loader').show();
  },
  complete: function(){
     $('#loader').hide();
  },
  success: function() {}
});



Async Controllers (MVC), long running process with “stops”

I was trying to solve something similar (reporting real-time progress from a long running server operation back to the client) recently and it turned out SignalR is a perfect fit for this situation.

Basically it is a library wrapping long-polling and Web Sockets, using (transparently) whatever is available on server and client.

I only have good experience with it so far.




This article seems to describe what you want, simple and w/o SignalR:

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

Controller:

public class HomeController : Controller { private static IDictionary tasks = new Dictionary();

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

});





Tags