dotnet / docs

This repository contains .NET Documentation.
https://learn.microsoft.com/dotnet
Creative Commons Attribution 4.0 International
4.18k stars 5.84k forks source link

[Question] Misunderstands "code that is asynchronously waiting for a canceled task through use of language features continues to run" #30896

Closed anchurcn closed 1 year ago

anchurcn commented 1 year ago

Any code that is asynchronously waiting for a canceled task through use of language features continues to run but receives an OperationCanceledException or an exception derived from it.

I don't know what it means.

I writes a demo to help me to understand but it only prints out "start".

public async Task CanceledTask()
. {
.     throw new TaskCanceledException();
. }
> public async Task ExceptionTask()
. {
.     throw new InvalidOperationException(); 
. }
public async Task TestAsync()
. {
.     WriteLine("start");
.     await CanceledTask();
.     WriteLine("Canceled task completed");
.     await ExceptionTask();
.     WriteLine("Exception task completed");
. }

Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

gewarren commented 1 year ago

I think this is referring specifically to asynchronous (TAP) methods that accept a CancellationToken. If there is a cancellation request and the TAP method ends the work and returns a task in the Canceled state, then the calling/awaiting code continues to run. Hopefully @BillWagner can explain further if needed.

BillWagner commented 1 year ago

Hi @anchurcn

We should clarify this language. The phrase "receives an OperationCancelledException ..." isn't as clear as it should be. When code awaits a task in the Canceled state, that exception is thrown. the code awaiting the task must catch the exception, or it will propagate up the call stack.

anchurcn commented 1 year ago

@BillWagner By learning and practicing, I seems to be clear.

public async Task TestAsync()
{
await Step1Async();
Step2();
}

Previous method is some magic of following. So I guess the doc means we can continue to run Step2, so the exception on Step1 can be caught on Step2.

public Task TestAsync()
{
return Step1Async().ContinueWith(Step2);
}

So we can write async code via TAP to do things like sync control flow. Any code after Step1 is wrap to Step2 and put into ContinueWith() and on internal view, so it looks like catch the exception on Step2.

public async Task TestAsync()
{
try{
await Step1Async();
}catch(Exception e)
{
 }
Step2Internal();
}
BillWagner commented 1 year ago

that's close. You'd need to write the catch clause in TestAsync the way you have it above. If you don't include the try / catch blocks, The exception thrown when Step1Async is cancelled prevents Step2Internal from ever being called.

That's why you original example only printed "start"

anchurcn commented 1 year ago

Thanks for your help @BillWagner. Because our docs are talking TAP on a higher level, does we need to use more accurate words on the docs?