I worked on a ASP.NET MVC 4 project in which users can apply filter on UI & extract an excel report. The data was stored in MS SQL server. To improve performances, i decided to adopt the async in my application to:
- Reduce the time of extraction
 - Allow users to have parallel extraction
 
To do this, my approach are:
When user click extraction button on UI -> an
ajaxcall will be made from client toasyncfunction on server -> Thisasyncfunction in turn will create anCommandobject & doExecuteReaderAsync(). Using thisDbDatareaderto generated an Excel file usingNPOIand save the file content toTempData. The handler to retrieve file will be return to client for later download usingwindow.location. I adopted these techniques from this post Download Excel file via AJAX MVCAfter the first extraction, if users want to extract another datasets in parallel, they can click extraction button again and application will repeat step 1.
The results are 2 or more data extractions can happened on the same time.
My problem is, take example, 4 extractions currently running in parallels, if any of these extractions finished & 1 file is downloaded (using window.location). The next time user click on extraction button (which repeat step 1), it doesn't async anymore & later extractions will wait for previous extraction finish before execute.
On debugging, if i restart the ISS server, the problem gone for a while until 1 file is downloaded, so I doubted that window.location do something that blocked the threads on server when any of file is downloaded.  
UPDATE 1
Class:
public class QUERYREADER
{
    public DbConnection   CONNECTION { get; set; }
    public DbDataReader   READER { get; set; }
}
Model:
public async Task<QUERYREADER> GET_DATA(CancellationToken ct)
{
    //Create the query reader
    QUERYREADER qr  = new QUERYREADER();
    //Set up the database instances
    DbProviderFactory dbFactory = DbProviderFactories.GetFactory(db.Database.Connection);
    //Defined the query
    var query = "SELECT * FROM Table";
    //Set up the sql command object
    using (var cmd = dbFactory.CreateCommand())
    {
        //Try to open the database connection 
        try
        {
            //Check if SQL connection is set up
            if (cmd.Connection == null)
            {
                cmd.CommandType = CommandType.Text;
                cmd.Connection  = db.Database.Connection;
            } 
            //Open connection to SQL if current state is closed
            if (cmd.Connection.State == ConnectionState.Closed)
            {
                //Change the connection string to set the packet size to max. value = 32768 to improve efficiency for I/O transmit to SQL server 
                cmd.Connection.ConnectionString = cmd.Connection.ConnectionString + ";Packet Size=20000";
                //Open connection
                await cmd.Connection.OpenAsync(ct);
            }
            //Save the connection 
            qr.CONNECTION = cmd.Connection;
        } catch (Exception ex) {
            //If errors throw, close the connection
            cmd.Connection.Close();
        };
        //Retrieve the database reader of provided sql query
        cmd.CommandText = query;       
        DbDataReader dr = await cmd.ExecuteReaderAsync(ct);
        qr.READER.Add(dr);       
    }  
    //Return the queryreader
    return qr;
}
Controller:
  public async Task<JsonResult> SQL_TO_EXCEL()
  {        
       //Set up the subscription to client for "cancellation request, browser closing"
        CancellationToken   disToken = Response.ClientDisconnectedToken;             
        //Get the datareader
        try 
        {
            qr  = await GET_DATA(disToken);        
        } 
        catch(Exception ex) { }
        //Open the connection to SQL server
        using (qr.CONNECTION)
        {
            using (var dr = qr.READER)
            {                                            
                while (await dr.ReadAsync(disToken))
                {
                    for (int k = 0; k < dr.FieldCount; k++)
                    {
                        //.... using NPOI to write Excel file to MemoryStream
                    }
                }
                dr.Close();
            }
        }
        //Generate XL file if controller action is still running (no "cancellation request, browser closing")
        if (!disToken.IsCancellationRequested)
        {
            string file_id = Guid.NewGuid().ToString();
            //... Write the NPOI excel file to TempData and then create a handler for later download at client
            //This line caused trouble
            TempData["file_id"] = XLMemoryStream.ToArray();
            HANDLER["file_id"]      = file_id;
            HANDLER["file_name"]    = FILE["FILE_NAME"].ToString().NonUnicode() + FILE["FILE_TYPE"].ToString() ;
        }
        //Return JSON to caller
        var JSONRESULT                  = Json(JsonConvert.SerializeObject(HANDLER), JsonRequestBehavior.AllowGet);
            JSONRESULT.MaxJsonLength    = int.MaxValue;
        return JSONRESULT;
  }
    public async Task<ActionResult> DOWNLOAD_EXCEL(string file_id, string file_name)
    {
        if (TempData[file_id] != null)
        {
            byte[] data = await Task.Run(() => TempData[file_id] as byte[]);
            return File(data, "application/vnd.ms-excel", file_name);
        }
        else
        {
            return new EmptyResult();
        }
    }   
Javascript
    $.ajax({
        type: 'POST',
        async: true,
        cache: false,
        url:  'SQL_TO_EXCEL',
        success: function (data)
        {
            var response = JSON.parse(data);
            window.location =
            (
                "DOWNLOAD_EXCEL"    +
                '?file_id='         + response.file_id +
                '&file_name='       + response.file_name
            );
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            console.log(errorThrown);
        }
    });    
UPDATE 2:
After a lot of tests, i figured out window.location has nothing to do with threads on server, the line TempData[file_id] = XLMemoryStream.ToArray() caused the issues. It look likes the problem is similar as described in this post Two parallel ajax requests to Action methods are queued, why?