VB.NET Async Function ignores CancellationToken despite CancelAfter timeout
0 reputation · 02 Nov 2022, 14:00 UTC
0 reputation · 02 Nov 2022, 14:00 UTC
In VB.NET on .NET Framework 4.5 and later, an Async Function that accepts a CancellationToken honors cancellation only cooperatively: the token must be forwarded to each awaitable call, such as Await Task.Delay(interval, ct), or checked through IsCancellationRequested. When the token is accepted but never passed along, a CancellationTokenSource configured with CancelAfter still fires on schedule while the method continues to its natural completion, so the intended timeout has no observable effect.
The unresolved design point is how the method should observe the token: rely on OperationCanceledException propagating from the awaited calls, or explicitly test IsCancellationRequested before each await and exit the loop without throwing. The exception approach centralizes handling but obligates every caller to catch it, while the polling approach reads linearly but is easy to omit at one of the await points. Blocking on the resulting task with .Result or .Wait is excluded in UI contexts because the continuation can deadlock.
Which observation pattern should a VB.NET async method use so cancellation is both reliable and readable? When is CancelAfter the right tool compared with Await Task.Delay(timeout, token)? Where should OperationCanceledException be caught so a caller can distinguish a timeout from other failures?
32275 reputation · 02 Nov 2022, 18:42 UTC
Forward the CancellationToken to every awaitable call and let OperationCanceledException propagate. Use CancellationTokenSource.CancelAfter (usually via a linked token source) when a single timeout should govern a whole sequence of operations. Catch OperationCanceledException at the UI boundary – e.g. in the async event handler or a wrapper that translates it – so the caller can tell a timeout from other failures by comparing the exception’s token with the timeout source.
CancelAfter only sets the token to signaled; it does not abort running code.Task.Delay(interval, ct) and any I/O API that accepts a token will throw TaskCanceledException (derived from OperationCanceledException) when the token is signaled.Async Function DoWorkAsync(ct As CancellationToken) As Task.ct to each awaitable: Await SomeIoAsync(ct) or Await Task.Delay(interval, ct).Using linkedSrc = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutSrc.Token) and pass linkedSrc.Token to the awaits.timeoutSrc.CancelAfter(TimeSpan.FromSeconds(5)) before calling the async method.Try block and catch oce As OperationCanceledException. Use a when‑filter or an inner If to test oce.CancellationToken Is timeoutSrc.Token to distinguish a timeout from a caller‑initiated cancellation.Missing diagnostic detail: Do the awaited operations inside your async method expose a CancellationToken parameter? If they do not, forwarding the token is impossible and you must poll ct.IsCancellationRequested (or call ct.ThrowIfCancellationRequested()) before each await and at loop boundaries instead.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.