You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In contrast to its sibling, Async.StartAsTask (because it is not starting a thread), Async.StartImmediateAsTask does not have a taskCreationOptions optional parameter, so people often use it pipeline expressions.
However, that makes it easy to gloss over the fact that the computation will then not have an ambient cancellation token (and be tied to DefaultCancellationToken).
In order to resolve those forces (wanting to be able to execute an async via piping, without the risk of omitting to consider cancelation), it is proposed to have a common helper function (with a lower case name) that
takes a cancellation token (as its first argument)
passes that and the Async onto Async.StartImmediateAsTask
Current proposed APIs (will be updated inline based on any discussion below):
module Async =
let inline startImmediateAsTask ct (a : Async<'t>) = Async.StartImmediateAsTask(a, cancellationToken = ct)
// ALTERNATELY
let inline executeAsTask ct (a : Async<'t>) = Async.StartImmediateAsTask(a, cancellationToken = ct)
Thanks for the detailed proposal, @bartelink! A few observations after reviewing the current codebase:
Current state
The library currently has AsyncExtensions (adds for x in taskSeq support inside async {}) and TaskExtensions (same for task {}), but no general TaskEx/Async-shim module yet. Issue #139 tracks that top-level effort.
The value it adds is real: forcing the cancellation token to be an explicit positional argument makes it impossible to omit by accident (unlike the optional ?cancellationToken parameter on Async.StartImmediateAsTask itself). The ct-first, async-second ordering also enables clean pipelining.
Naming
Between startImmediateAsTask and executeAsTask:
startImmediateAsTask mirrors the BCL name, which aids discoverability.
executeAsTask is more concise but obscures the "runs on the current synchronisation context" semantics.
My lean is toward startImmediateAsTask for consistency with F# Core naming conventions.
Issue #141 (and the open PR #321) addresses the complementary direction — Async.ofTask / awaitTaskCorrect — properly unwrapping AggregateException when bridging Task → Async. Together, Async.ofTask and Async.startImmediateAsTask form a tidy bidirectional bridge.
Implementation readiness
This is straightforward to implement. It would fit naturally alongside whatever comes out of #139/#141 as a small TaskEx or Async-extension module. The main open question is whether this belongs in this library at all (vs. a standalone shim package), but given #139 is already tracking broader TaskEx additions, this seems in scope.
startImmediateAsTask with only a casing difference would be a bad idea (especially as there is a subtle parameter re-ordering involved)
but it does put cancellation front and center which aligns with that principle.
So, where would you fall on this one?
Can't/shouldn't do, even though it removes a pitfall and ugliness from a relatively common ask, it's not interesting enough for FSharp.Core, and having a PascalCase and a camelCase version of the same thing just makes things confusing
As with the general observation that doing Async.Await will likely establish useful precedents for Task.ignore and friends, perhaps Task.ignore can inform the decision process here?
Closing as a pipeline friendly function that forces a CancellationToken to be supplied is included in fsharp/fslang-suggestions#1467 (Task.runAsyncTask.startAsyncImmediate)
Activity
[-]TaskShims: Async.startImmediateAsTask[/-][+]TaskEx: Async.startImmediateAsTask[/+]github-actions commentedon Mar 10, 2026
🤖 This is an automated response from Repo Assist.
Thanks for the detailed proposal,
@bartelink! A few observations after reviewing the current codebase:Current state
The library currently has
AsyncExtensions(addsfor x in taskSeqsupport insideasync {}) andTaskExtensions(same fortask {}), but no generalTaskEx/Async-shim module yet. Issue #139 tracks that top-level effort.On the proposed API
The core proposal is essentially a one-liner:
The value it adds is real: forcing the cancellation token to be an explicit positional argument makes it impossible to omit by accident (unlike the optional
?cancellationTokenparameter onAsync.StartImmediateAsTaskitself). Thect-first,async-second ordering also enables clean pipelining.Naming
Between
startImmediateAsTaskandexecuteAsTask:startImmediateAsTaskmirrors the BCL name, which aids discoverability.executeAsTaskis more concise but obscures the "runs on the current synchronisation context" semantics.My lean is toward
startImmediateAsTaskfor consistency with F# Core naming conventions.Relationship to #141
Issue #141 (and the open PR #321) addresses the complementary direction —
Async.ofTask/awaitTaskCorrect— properly unwrappingAggregateExceptionwhen bridgingTask → Async. Together,Async.ofTaskandAsync.startImmediateAsTaskform a tidy bidirectional bridge.Implementation readiness
This is straightforward to implement. It would fit naturally alongside whatever comes out of #139/#141 as a small
TaskExorAsync-extension module. The main open question is whether this belongs in this library at all (vs. a standalone shim package), but given #139 is already tracking broaderTaskExadditions, this seems in scope.bartelink commentedon Mar 18, 2026
@T-Gro this issue straddles the first two concerns in our context setting to/fro:
startImmediateAsTaskwith only a casing difference would be a bad idea (especially as there is a subtle parameter re-ordering involved)So, where would you fall on this one?
FSharp.Core, and having a PascalCase and a camelCase version of the same thing just makes things confusingexecuteAsTask, it is toolable and has clear semantics. The naming and semantics should align with Add Async.RunImmediate fsharp/fslang-suggestions#1042As with the general observation that doing
Async.Awaitwill likely establish useful precedents forTask.ignoreand friends, perhapsTask.ignorecan inform the decision process here?bartelink commentedon May 9, 2026
Closing as a pipeline friendly function that forces a
CancellationTokento be supplied is included in fsharp/fslang-suggestions#1467 (Task.runAsyncTask.startAsyncImmediate)