0

I have an Mvc Web Api and I'm calling a long running action (1 minute) with async jquery but every thing will get blocked until the result is back.

this is my Action

 [System.Web.Http.HttpGet]
    public async Task<JsonResult> GetParsedApplicationsAsync(string className, string actionName)
    {
        var userRoles = Users.Profile.GetCurrentOrNew().Roles.Select(x => x.Id);
        var sss = AppDomain.CurrentDomain.GetAssemblies();
        var assemblies = sss
            .SelectMany(x => x.GetTypes())
            .Where(x => typeof(IAppSearch).IsAssignableFrom(x));


        var l = new List<decimal?>();
        foreach (var userRole in userRoles)
        {
            l.Add(userRole);
        }
        var apps =
            new VwApplicationsDetailsDap().GetByRoleIds(l.ToArray(), Request.QueryString, "", pagenation: false);


        // Long Running method
        var action = await Task.Factory.StartNew(() =>
        {
            // create instance of class 
            var type = assemblies.FirstOrDefault(x => x.Name == className);
            var instance = Activator.CreateInstance(type, apps);
            return type.GetMethod(actionName).Invoke(instance, BindingFlags.IgnoreCase, null, null, null);

        });
        return await Task.FromResult(Json(action, JsonRequestBehavior.AllowGet));

    }

And I tried also var action = await Task.Run(() => Instead of await Task.Factory.StartNew

What can i do ?

Nour Berro
  • 550
  • 5
  • 14
  • On server-side request will be "blocked" - response will be send only when you finish your long running process. In ASP.NET `async-await` will only free current thread during "awaiting" and continue execution when task is complete. From client-side point of view request-response will be synchronous. On client-side you can use ajax which makes call in asynchronous way. – Fabio Apr 09 '17 at 09:49
  • @Fabio i want the app response to other request during the long running process, thats why i used async/await in the first place – Nour Berro Apr 09 '17 at 09:57
  • 2
    ASP.NET Web API 2 will response on other requests even without `async/await` – Fabio Apr 09 '17 at 10:00
  • 1
    Async/await does not what you think. Please take time to understand how async/await works in C#. Also, using await on `Task.Run` or `Task.Factory.StartNew` is a big mistake. This resource is a good start: https://blog.stephencleary.com/2012/02/async-and-await.html – Federico Dipuma Apr 09 '17 at 10:24
  • @FedericoDipuma, using `await` with `Task.Run` is ok in situation where task executes some long running action without external resources involved. But it will be wasting of threads when you using `Task.Run` only for sending some requests and waiting for response, like database queries, reading file system or web services – Fabio Apr 09 '17 at 12:27
  • 1
    @Fabio there is no good reason in using `Task.Run` with `await` inside a web request lifecycle, it will only hinder performance, [this question will clarify further](https://stackoverflow.com/questions/33764366/is-task-run-considered-bad-practice-in-an-asp-net-mvc-web-application). The only good usage for awaiting a long running task (CPU bound) created using `Task.Run` is when [you do not want to block UI thread](https://blog.stephencleary.com/2013/10/taskrun-etiquette-and-proper-usage.html) (e.g. in desktop apps). – Federico Dipuma Apr 09 '17 at 12:45
  • 1
    @FedericoDipuma - agree about web api. – Fabio Apr 09 '17 at 13:01
  • Ok i got it guys thanks – Nour Berro Apr 10 '17 at 11:40

1 Answers1

0

It looks like your long running method is being dynamically invoked, and you are awaiting the result in your async method, which will the thread back to the thread pool.

However because you are using Task.Factory.StartNew, you are starting work on a new thread, and running the work synchronously there which is blocking a thread (this is a little bit worse than invoking the blocking code directly from GetParsedApplicationsAsync).

The bottom line is if your response requires the results of this long running method, you should make this method async, then when you dynamically invoke it, cast the result to Task<T> and await the result.

Final tip, you can just return the result instead of await Task.FromResult(...), so your last line should read:

return Json(action, JsonRequestBehavior.AllowGet);
Stuart
  • 5,358
  • 19
  • 28