我有以下两个应用程序
我正在使用 Angular 的 HttpClient 向 API 发出 GET 请求,如下所示
this.subscription = this.httpClient.get('api/Controller/LongRunningProcess')
.subscribe((response) =>
{
// Handling response
});
API控制器的LongRunningProcess方法有以下代码
[HttpGet]
[Route("LongRunningProcess")]
public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)
{
try
{
// Dummy long operation
await Task.Factory.StartNew(() =>
{
for (int i = 0; i < 10; i++)
{
// Option 1 (Not working)
if (cancellationToken.IsCancellationRequested)
break;
// Option 2 (Not working)
cancellationToken.ThrowIfCancellationRequested();
Thread.Sleep(6000);
}
}, cancellationToken);
}
catch (OperationCanceledException e)
{
Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {e.Message}");
}
return Ok();
}
现在我想取消这个长时间运行的过程,所以我从客户端取消订阅,如下所示
// On cancel button's click
this.subscription.unsubscribe();
上面的代码将取消请求,我可以在浏览器的网络选项卡中看到它被取消,如下所示
但不会在 API 的 LongRunningProcess 方法中将 IsCancellationRequested 设为 true,因此操作将继续进行。
[注意]: 即使我使用 postman 进行调用,API 方法中的 Option 1 和 Option 2 都不起作用。
问题:有什么办法可以取消那个LongRunningProcess方法的运行吗?
在这种情况下你不需要中断,只需像这样使用
[HttpGet]
[Route("LongRunningProcess")]
public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)
{
for (int i = 0; i < 10; i++)
{
cancellationToken.ThrowIfCancellationRequested();
// Dummy long operation
await Task.Factory.StartNew(() => Thread.Sleep(60000));
}
return Ok();
}
为了进行虚拟长操作,代码如下
[HttpGet]
[Route("LongRunningProcess")]
public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)
{
// Dummy long operation
await Task.Run(() =>
{
for (var i = 0; i < 60; i++)
{
if (cancel.IsCancellationRequested)
break;
Task.Delay(1000).Wait();
}
});
return Ok();
}
顺便说一句,
Task.Run
相当于
Task.Factory.StartNew
。但是,如果您只需要在 Web API 中进行虚拟的长期操作,那么您也可以简单地使用
Task.Delay
,它支持取消令牌。
Task.Delay
取消请求时会抛出异常,因此当请求取消后需要执行某些操作时,请添加异常处理代码。
[HttpGet]
[Route("LongRunningProcess")]
public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)
{
// Dummy long operation
await Task.Delay(60000, cancel);
return Ok();
}
提示:订阅包含一个封闭的布尔属性,在高级情况下可能很有用。对于 HTTP,这将在完成时设置。在 Angular 中,在某些情况下在 ngDestroy 中设置 _isDestroyed 属性可能很有用,您的订阅处理程序可以检查该属性。
提示 2:如果处理多个订阅,您可以创建一个临时的 new Subscription() 对象并向其添加(...)任何其他订阅 - 因此,当您从主订阅取消订阅时,它也会取消所有添加的订阅。
因此,最佳实践是使用 takeUntil() 并在组件被销毁时取消订阅 http 调用。
import { takeUntil } from 'rxjs/operators';
.....
ngOnDestroy(): void {
this.destroy$.next(); // trigger the unsubscribe
this.destroy$.complete(); // finalize & clean up the subject stream
}
var cancellationToken = new CanellationToken();
cancellationToken.CancelAfter(2000);
using (var response = await _httpClient.GetAsync("emp",
HttpCompletionOption.ResponseHeadersRead, cancellationTokenSource.Token))
{
response.EnsureSuccessStatusCode();
var stream = await response.Content.ReadAsStreamAsync();
var emp = await JsonSerializer.DeserializeAsync<List<empDto>>(stream, _options);
}
此外,我们还可以有这个“CancellationToken”类,它只是一个 Http 客户端方法,它会在一定时间间隔后终止请求。
[HttpGet]
[Route("LongRunningProcess")]
public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)
{
// Combine the provided cancellation token with the HttpContext.RequestAborted token
CancellationToken combinedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, HttpContext.RequestAborted).Token;
try
{
// Dummy long operation using combined cancellation token
await Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
// Option 1: Check if cancellation is requested
if (combinedToken.IsCancellationRequested)
break;
// Option 2: Throw an exception if cancellation is requested
combinedToken.ThrowIfCancellationRequested();
Thread.Sleep(6000); // Simulating long-running task
}
}, combinedToken);
}
catch (OperationCanceledException e)
{
Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {e.Message}");
return StatusCode(499); // Custom status code for canceled request
}
return Ok("Operation completed successfully.");
}