Uh oh!
There was an error while loading. Please reload this page.
feat(Async): RunSynchronouslyImmediate - #19804
Conversation
❗ Release notes requiredYou can open this PR in browser to add release notes: open in github.dev
|
d6a0470 to
69e41a2CompareThere was a problem hiding this comment.
Pull request overview
Adds Async.RunSynchronouslyImmediate to FSharp.Core to allow running an Async<'T> synchronously while always executing the initial step on the calling thread (aimed at improved diagnostics/stack traces in FSI/tests), with accompanying API surface updates, documentation, unit tests, and release notes.
Changes:
- Add public API
Async.RunSynchronouslyImmediateand wire it through Async primitives. - Add unit tests characterizing basic behavior and key differences vs
Async.RunSynchronously. - Update FSharp.Core surface area baselines and release notes; extend XML docs for
RunSynchronously/RunSynchronouslyImmediate.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs | Adds unit tests for RunSynchronouslyImmediate and contrasts with RunSynchronously. |
| tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl | Records new public surface area entry for RunSynchronouslyImmediate. |
| tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl | Records new public surface area entry for RunSynchronouslyImmediate. |
| tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl | Records new public surface area entry for RunSynchronouslyImmediate. |
| tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl | Records new public surface area entry for RunSynchronouslyImmediate. |
| src/FSharp.Core/async.fsi | Adds XML docs for the new API and revises RunSynchronously docs to reference it. |
| src/FSharp.Core/async.fs | Implements the new API by exposing an “immediate” synchronous runner and refactoring RunSynchronously internals. |
| docs/release-notes/.FSharp.Core/11.0.100.md | Adds release note entry for Async.RunSynchronouslyImmediate. |
Comments suppressed due to low confidence (1)
tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs:527
- This test uses a raw Thread but doesn’t capture exceptions from the thread body. Any unexpected exception inside the thread (including from Async.RunSynchronously) will be unhandled and may crash the test process rather than reporting a normal xUnit failure. Capture exceptions in the thread and rethrow/assert after Join.
let t = Thread(fun () ->
callerThreadId <- Thread.CurrentThread.ManagedThreadId
async { runSyncThreadId <- Thread.CurrentThread.ManagedThreadId }
|> Async.RunSynchronously
async { immThreadId <- Thread.CurrentThread.ManagedThreadId }
|> Async.RunSynchronouslyImmediate)
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
@T-Gro I've implemented as per OP of fsharp/fslang-suggestions#1042 Firstly, in a debugger context a first chance exception breakpoint has more direct causation vs having to disentangle an adjacent thread waiting, which tooling may or may not be able to convey unaided. However, while the exception stack trace has less noise, it's not significantly better AFAICT: ![]() It seems the TL;DR value prop is:
But its not a slam dunk as:
This is making me think I should dial back the xmldoc from where I have it trying to set a "use the new RunSynchronouslyImmediate in FSI from now on" vibe (as I've tried to do with AwaitTask vs Await where it is a slam dunk) |
Uh oh!
There was an error while loading. Please reload this page.
75fed30 to
f9e0383CompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
bartelink
commented
May 27, 2026
The open review comments and Don's comment here fsharp/fslang-suggestions#1042 (comment) make me think:
In general I think one PR that adds one API is a perfectly fine outcome for now, but if someone out there (@dsyme ?) has a really clear picture of all the use cases, it may be worth at least speccing out in full the orthogonal elements of what only the current impl of In short, if someone is convinced that RSI + RSB and/or some complementary APIs makes it possible to deprecate and eventually move away from from a complected |
majocha
commented
May 27, 2026
FWIW my limited experience with internal |
Uh oh!
There was an error while loading. Please reload this page.
T-Gro
commented
Jun 4, 2026
@bartelink : And consider marking it obsolete later in a separate PR. What RunSynchronously provides is the automated decision-making based on inputs+context. Even though it can be handy when writing library code (that does not know where/how it will be run), it does go against the "Pit of success" spirit. |
bartelink
commented
Jul 13, 2026
@T-Gro I believe this is ready for re-review; feel free to mark comment threads as resolved as you read them. Happy to address any follow-ups later and/or tomorrow |
T-Gro
left a comment
There was a problem hiding this comment.
Green CI and the review is essentially done — ready to approve once the last small items land:
- shim guard (
Common.fs,Utilities.fs): drop the#ifand always include the shim until an 11.xFSharp.Coreships the member (per the thread — the constant is never defined for the test projects, so the guard never toggled). async.fsi:50RS summary: drop "honoring the ambientSynchronizationContext" per the thread.
Ping me when those are in and I'll approve.
T-Gro
commented
Jul 21, 2026
Green CI and the review is essentially done — ready to approve once the last small items land:
Ping me when those are in and I'll approve. |
@T-Gro I believe this is ready for final review; the above two points have been addressed (see #19804 (comment)) |
T-Gro
commented
Aug 4, 2026
🤖🕵️ |
bartelink
commented
Aug 4, 2026
@T-Gro will merge latest, adjust any straggler RunImmediates and @ you later when I get a chance |
bartelink
commented
Aug 6, 2026
@T-Gro looks like this has finally made it ;) |
Uh oh!
There was an error while loading. Please reload this page.
T-Gro
commented
Aug 6, 2026
@bartelink : Thank you for your persistence and congratulations 👍 |
- rename and sync clone impls withing VisualFSharp.slnx as per previous PR - update RunImmediateExceptOnUI to delegate and follow naming
- rename and sync clone impls withing VisualFSharp.slnx as per previous PR - update RunImmediateExceptOnUI to delegate and follow naming


Implements
RunSynchronouslyImmediateper fsharp/fslang-suggestions#1042See also fsharp/fslang-suggestions#1467
Heavily revises the xmldoc for
RunSynchronouslyin order to convey the tradeoffs involved in selecting between the new and the old.Updated

RunSynchronously:NEW

RunSynchronouslyImmediate:NOTE xmldocs are intended to convey a nuanced message as explained in detail in #19804 (comment):
AsyncResult.CommitandQueueAsyncAndWaitForResultSynchronouslyChecklist
Async.RunSynchronously