Skip to main content

Don't let the browser hang while waiting for the processing... create a background process queue and display page.

---
title: Long Running Background Tasks in ASP.NET
author: Alan D. Jackson
date: April 11, 2012
source: https://alandjackson.wordpress.com/2012/04/11/long-running-background-tasks-in-asp-net-mvc3/
notoc: false
---

Often I would like a web request to generate some content for the user (usually
a report or download).  I don't want the browser to hang while it waits for the
processing to occur and I don't want to deal with web timeouts.  It's also not
involved enough that I want to create an entire background process queue and
display page.

In order to accomplish this, I have a basic async pattern:

1. Start the process
2. Poll the process every so often to see if it is done
3. When it is done, display the results to the user

The pattern needs to be implemented in two places: on the server side in c# and on the client side in javascript.

To start out with, lets create a basic web form that the user is going to use to kick off the process:

![Kick off some process...](assets/long_running_background_tasks_in_aspnet/form-start.jpg)

## CSS and HTML

Generated with this css and html:

```html
<style>
    input[type="button"], .button
    {
        border: 1px solid #aaa;
        padding: 6px 12px 6px 12px;
        background-color: #F0F0F0;
        color: Black;
        margin: 2px;
    }
    .button img
    {
        padding-top: -3px;
    }
</style>

<form>
    <div style="margin-bottom:.5em;">
        <span>Name: </span> <input type="text" name="Name" value="Bob" />
    </div>
    <div>
        <span>Sleep: </span> <input type="text" name="Sleep" value="2" />
    </div>

    <div style="margin-top:2em;">
        <input type="button" id="myButton" value="Start Long Running Process" />

        <span class="button" id="myButtonProgress" style="display:none;">
            Running Process...
            <img src="../../Content/ajax-loader.gif" />
        </span>
    </div>
</form>

<div id="msg" style="margin-top:2em;"></div>
```

The javascript handles submitting the form, checking the process, and displaying
it to the user:

## JavaScript

```js
var processId;

function startProcess() {
    $.ajax({
        type: "POST",
        url: "/Task/Begin",
        data: $("form").serialize(),
        success: function (data) {
            $('#myButton').toggle();
            $('#myButtonProgress').toggle();
            processId = data;
            window.setTimeout(checkProcess, 1000);
        },
        error: function (jqXHR, textStatus, errorThrown) {
            $('#msg').html("Error starting process: " + jqXHR.status + " " + errorThrown);
        }
    });
}

function checkProcess() {
    $.ajax({
        url: "/Task/GetStatus?id=" + encodeURI(processId),
        success: function (data) {
            if (data.toString() == "Done") {
                endProcess();
            } else {
                window.setTimeout(checkProcess, 1000);
            }
        },
        error: function (jqXHR, textStatus, errorThrown) {
            $('#msg').html("Error checking process: " + jqXHR.status + " " + errorThrown);
            $('#myButton').toggle();
            $('#myButtonProgress').toggle();
        }
    });
}

function endProcess() {
    $('#myButton').toggle();
    $('#myButtonProgress').toggle();

    $.ajax({
        url: "/Task/End?id=" + encodeURI(processId),
        success: function (data) {
            $('#msg').html(data);
        },
        error: function (jqXHR, textStatus, errorThrown) {
            $('#msg').html("Error getting process results: " + jqXHR.status + " " + errorThrown);
        }
    });
}

$(document).ready(function () {
    $('#myButton').click(function () {
        startProcess();
    });
});
```

## TaskController

I have assumed that you are going to use a controller called Task to handle the
server side, but these actions can be added to any controller:

```cs
public class TaskController : Controller
{
    static Dictionary<string, Task<string>> Tasks = new Dictionary<string, Task<string>>();

    public ActionResult Begin(FormCollection form)
    {
        string taskId = Guid.NewGuid().ToString();
        Tasks[taskId] = Task.Factory.StartNew<string>(() => RunTask(form));

        return Content(taskId);
    }

    public ActionResult GetStatus(string id)
    {
        if (!Tasks.ContainsKey(id))
            return Content("Unknown Task");

        if (Tasks[id].IsCompleted)
            return Content("Done");

        return Content("Running");
    }

    public ActionResult End(string id)
    {
        if (!Tasks.ContainsKey(id))
            return Content("Unknown Task");

        if (!Tasks[id].IsCompleted)
            Task.WaitAll(Tasks[id]);

        return Content(Tasks[id].Result);
    }

    private string RunTask(FormCollection form)
    {
        try
        {
            string name = form["Name"];
            string sleep = form["Sleep"];

            int secondsToSleep;
            if (!int.TryParse(sleep, out secondsToSleep) || secondsToSleep <= 0 || secondsToSleep > 5)
                secondsToSleep = 3;

            Thread.Sleep(secondsToSleep * 1000);

            return name + " requested that I sleep for " + sleep + " seconds.";
        }
        catch (Exception e)
        {
            return "Error: " + e.Message;
        }
    }
}
```

## Example on GitHub

A complete example project can be found on [GitHub](https://github.com/alandjackson/AsyncTask).