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

If you have long running (server side) tasks in your ASP.NET MVC 3 web application it could be reasonable to provide the user with information about the current progress of this task. In this blog post I will show how such a progress indicator could be implemented using jQuery and AJAX.

The basic idea is as follows: Two actions exists on the server side; one action starts the (long running) task in a new thread and returns a unique identifier (GUID in this example) to the client and the second action returns the current (percentaged) status for a specific task.

For simplicity, in this example all (server side) functionality is implemented in the HomeController:

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

The HomeController uses a Dictionary to save the progresses of all tasks. The Start action adds a new entry to this dictionary (with progress 0) and starts a new Task that simulates the long running operation. It is important to understand that this (time-consuming) operation is running in a separate thread and server immediately returns a result to the client (the unique identifier of the task), i.e. there is no HTTP request that is open as long as the task runs. The Progress action just returns the progress for a given GUID that uniquely identifies a task. Both actions return a JSON-result to enable easily processing of the data on the client side.

The client side functionality is implemented in the Index.cshtml 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>

On the client side the jQuery function $.post() is used to

  • first “call” the URL Home/Start to start a new task
  • and then periodically pass the returned GUID to the URL Home/Progress to get the current progress.

To show some feedback to the user a <p> tag is created for each started task and periodically updated with the current progress.

Note: The approach shown above supports multiple parallel running tasks. You can click the “start” link while other task(s) are still running.

The code shown above is only exemplary. In a real-world project one should implement a robust error handling to support for example situations where the tasks are interrupted. Furthermore it could be reasonable to limit the number of parallel tasks one user could start, since long running tasks are often need much resources.

You can download the Visual Studio 2010 project containing all the source code here.

Comments

  1. Matt

    Implemented this today. Works great! Thanks.

  2. Rafael

    Great dude,
    I think that you can do this with SignalR for notifications.
    look here: https://github.com/rsantosdev/SignalR.LongRunningTasks

  3. Eka808

    Simple, Concise, Works great : implemented.
    Thank’s !!!!

  4. Swanand Kulkarni

    I m new in mvc, so it is hard to understand. If possible please provide complete source of above example. I have same senario in my project to import csv into database.

  5. Jan Jonas

    Thanks for your comment. You can download a VS2010 project from here.

  6. Roger

    Thank you for your post. It helped me a lot.
    I did have to change $.post(“Home/Start”, … to $.post(“Start”, …

  7. subburaj

    This is good detail about mvc6.

    ASP.Net Migration

  8. simon de la Reza

    Thanks man! This helped me a lot! Worked perfectly in MVC 4.

  9. Max

    Nice job. It helped me a lot. Thanks man.

  10. yaron

    very nice i liked it !

Leave a comment

Your email address will not be published. Required fields are marked *

*