Uh oh!
There was an error while loading. Please reload this page.
Align Async.Await with fslang-suggestions #840 (raise TaskCanceledException on cancel) - #677
Conversation
A cancelled Task is now surfaced through the exception continuation as a TaskCanceledException (ec) instead of the cancellation continuation (cc), so it can be caught by an ordinary try/with around the Await, matching C# await and Async.AwaitTask. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Aligns Async.Await’s cancellation semantics with fslang-suggestions #840 by surfacing a canceled Task as a catchable TaskCanceledException via the exception continuation (rather than treating it as async cancellation), and updates tests accordingly.
Changes:
- Updated
Async.Await(forTask<'T>andTask) to routeIsCanceledthrough the exception continuation (ec) withTaskCanceledException. - Updated async zip tests to use a truly canceled
Async(via cancellation continuation) rather than relying on awaiting a canceledTask. - Added tests asserting a canceled
Task/Task<'T>awaited viaAsync.Awaitraises aTaskCanceledExceptioncatchable bytry/with.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/FSharpPlus/Extensions/Extensions.fs | Changes Async.Await cancellation path to raise TaskCanceledException via exception continuation. |
tests/FSharpPlus.Tests/Asyncs.fs | Adjusts zip test inputs to produce genuine async cancellation independent of Async.Await behavior. |
tests/FSharpPlus.Tests/Task.fs | Adds coverage to ensure awaiting canceled tasks raises TaskCanceledException catchable by try/with. |
Suppressed comments (1)
src/FSharpPlus/Extensions/Extensions.fs:200
- Same as the generic overload: prefer
TaskCanceledException(task)overTaskCanceledException()so the thrown exception is associated with the awaited task (matching typicalawaitsemantics) and preserves the Task reference for diagnostics.
if task.IsFaulted then
let e = Unchecked.nonNull task.Exception
if e.InnerExceptions.Count = 1 then ec e.InnerExceptions[0]
else ec e
elif task.IsCanceled then ec (TaskCanceledException ())
else sc ())
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
wallymathieu
commented
Aug 11, 2026
Thanks @T-Gro for the contribution! 😃 |
Aligns
Async.Awaitwith fslang-suggestions #840, as requested in dotnet/fsharp#19785 (comment) and tracked by #676.What
On cancellation, both
Awaitoverloads (Task<'T>andTask) now call the exception continuation with aTaskCanceledException(ec) instead of the cancellation continuation (cc):Why
With
cc, a cancelledTaskpropagated as an async cancellation, so atry ... with :? TaskCanceledException ->placed around theAwaitwould not catch it. Withecit is surfaced as an ordinary exception, catchable locally — matching C#awaitand everyAwaitTaskCorrect-derived implementation. The doc comment already described this behaviour.Note:
Async.map2/map3/zipbuild onAwait, so a purely-cancelled input now surfaces as aTaskCanceledExceptionrather than a cancellation. Where cancellation is combined with a fault, the fault still wins (cancellation dropped inTask.map*before reachingAwait).Tests
Asyncs.fszip tests build their cancellation input directly (they previously relied onAwaitof a cancelledTaskyielding a cancellation).Task.fscoverage asserting a cancelledTask/Task<'T>await raises aTaskCanceledExceptioncatchable bytry/with.