Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Abort AJAX request via CancellationToken in ASP.NET MVC

I have an ASP.NET MVC application that interacts with external resources and there is an operation which takes a lot of time. So in controller I have method like this

[HttpPost]
public async Task<JsonResult> SomeMethod(..., CancellationToken token)
{
    await _someService.ExecuteSlowOperationAsync(..., token);
    ...
}

And this slow operation looks like

public async Task ExecuteSlowOperationAsync(..., CancellationToken token)
{
    return await Task.Run(() => 
    {
       //interacting with external resource
    }, token);
} 

This method linked with modal view and if request will take so much time user might decide to close it. According to that I have to cancel request without awaiting results, so on client-side I have code like

...
var request = $.ajax(...);

...

$('#modal').on('hidden.bs.modal', function () {
  request.abort();
});

If I understood this article correctly, cancellation token binds with request via framework model binder and no need to do something with it. When user close modal form, in browser concole I can see that request gets status "canceled" but on server-side slow operation is still executing. Aslo I tried with

CancellationToken disconnectedToken = Response.ClientDisconnectedToken;            
var source = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, disconnectedToken);

and then took token from this source, but still got nothing.

I feel like I'm missing some important thing and have misunderstanding of this situation. Any ideas how to make it work?

like image 871
frostnova Avatar asked May 30 '18 23:05

frostnova


1 Answers

I am probably a little late with my answer. But I think I found your problem. Using Task.Run(Action action, CancellationToken cancellationToken) to cancel your long running operation might not always work.

Using CancellationToken for timeout in Task.Run does not work

If the long running operation is in your own code, you should write something like this

public async Task ExecuteSlowOperationAsync(..., CancellationToken token)
{
    while (true)
    {
        if(cancelToken.IsCancellationRequested)
        {
            return;
        }
        ...
    }
}

If you cannot modify the source code of the long running operation itself, you can use WithCancellation from Microsoft.VisualStudio.Threading.ThreadingTools (see https://stackoverflow.com/a/33396646/9483821 for more info) which you even could use earlier at your controller

[HttpPost]
public async Task<JsonResult> SomeMethod(..., CancellationToken token)
{
     CancellationToken disconnectedToken = Response.ClientDisconnectedToken;            
     var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, disconnectedToken).Token;
     await _someService.ExecuteSlowOperationAsync(...).WithCancellation(linkedToken);
     ...
}
like image 103
Simon Avatar answered Sep 17 '22 08:09

Simon