Skip to content

Commit 1166af9

Browse files
committed
Add and use Async.AsTask
1 parent 2091c9e commit 1166af9

File tree

7 files changed

+70
-52
lines changed

7 files changed

+70
-52
lines changed

src/FSharpPlus/Control/Comonad.fs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,18 @@ open System.Runtime.InteropServices
55
open System.Threading.Tasks
66

77
open FSharpPlus
8+
open FSharpPlus.Extensions
89
open FSharpPlus.Internals
910
#if !FABLE_COMPILER4
1011

1112
// Comonad class ----------------------------------------------------------
1213

1314
type Extract =
14-
static member Extract (x: Async<'T> ) =
15+
static member Extract (x: Async<'T>) =
1516
#if FABLE_COMPILER_3 || FABLE_COMPILER_4
1617
Async.RunSynchronously x
1718
#else
18-
Async.StartImmediateAsTask(x).Result
19+
Async.AsTask(x).Result
1920
#endif
2021
static member Extract (x: Lazy<'T> ) = x.Value
2122
static member Extract ((_: 'W, a: 'T) ) = a

src/FSharpPlus/Control/Functor.fs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ open Microsoft.FSharp.Quotations
1111
open FSharpPlus.Internals
1212
open FSharpPlus.Internals.Prelude
1313
open FSharpPlus
14+
open FSharpPlus.Extensions
1415
open FSharpPlus.Data
1516

1617
#if (!FABLE_COMPILER || FABLE_COMPILER_3) && ! FABLE_COMPILER_4
@@ -21,7 +22,7 @@ type Iterate =
2122
static member Iterate (x: Lazy<'T> , action) = action x.Value : unit
2223
static member Iterate (x: seq<'T> , action) = Seq.iter action x
2324
static member Iterate (x: option<'T> , action) = match x with Some x -> action x | _ -> ()
24-
static member Iterate (x: voption<'T> , action) = match x with ValueSome x -> action x | _ -> ()
25+
static member Iterate (x: voption<'T>, action) = match x with ValueSome x -> action x | _ -> ()
2526
static member Iterate (x: list<'T> , action) = List.iter action x
2627
static member Iterate ((_: 'W, a: 'T), action) = action a :unit
2728
static member Iterate (x: 'T [] , action) = Array.iter action x
@@ -35,10 +36,10 @@ type Iterate =
3536
for l = 0 to Array4D.length4 x - 1 do
3637
action x.[i,j,k,l]
3738
#endif
38-
#if !FABLE_COMPILER
39+
#if FABLE_COMPILER
3940
static member Iterate (x: Async<'T> , action) = action (Async.RunSynchronously x) : unit
4041
#else
41-
static member Iterate (x: Async<'T> , action) = action (x |> Async.Ignore |> Async.StartImmediate) : unit
42+
static member Iterate (x: Async<'T> , action: 'T -> unit) = (x |> Async.map action |> Async.AsTask).Wait ()
4243
#endif
4344
static member Iterate (x: Result<'T, 'E> , action) = match x with Ok x -> action x | _ -> ()
4445
static member Iterate (x: Choice<'T, 'E> , action) = match x with Choice1Of2 x -> action x | _ -> ()

src/FSharpPlus/Control/Traversable.fs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ type Traverse =
8989
return seq {
9090
use enum = t.GetEnumerator ()
9191
while enum.MoveNext() do
92-
yield Async.RunSynchronously (f enum.Current, cancellationToken = ct) }}
92+
yield Async.AsTask(f enum.Current, cancellationToken = ct).Result }}
9393
#endif
9494

9595
#if !FABLE_COMPILER
@@ -102,7 +102,7 @@ type Traverse =
102102
return seq {
103103
use enum = t.GetEnumerator ()
104104
while enum.MoveNext() do
105-
yield Async.RunSynchronously (f enum.Current, cancellationToken = ct) } |> NonEmptySeq.unsafeOfSeq }
105+
yield Async.AsTask(f enum.Current, cancellationToken = ct).Result } |> NonEmptySeq.unsafeOfSeq }
106106
#endif
107107

108108
static member Traverse (t: Id<'t>, f: 't -> option<'u>, [<Optional>]_output: option<Id<'u>>, [<Optional>]_impl: Traverse) =
@@ -332,7 +332,7 @@ type Gather =
332332
return seq {
333333
use enum = t.GetEnumerator ()
334334
while enum.MoveNext() do
335-
yield Async.RunSynchronously (f enum.Current, cancellationToken = ct) }}
335+
yield Async.AsTask(f enum.Current, cancellationToken = ct).Result }}
336336
#endif
337337

338338
#if !FABLE_COMPILER
@@ -345,7 +345,7 @@ type Gather =
345345
return seq {
346346
use enum = t.GetEnumerator ()
347347
while enum.MoveNext() do
348-
yield Async.RunSynchronously (f enum.Current, cancellationToken = ct) } |> NonEmptySeq.unsafeOfSeq }
348+
yield Async.AsTask(f enum.Current, cancellationToken = ct).Result } |> NonEmptySeq.unsafeOfSeq }
349349
#endif
350350

351351
static member Gather (t: Id<'t>, f: 't -> option<'u>, [<Optional>]_output: option<Id<'u>>, [<Optional>]_impl: Gather) =

src/FSharpPlus/Extensions/Async.fs

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,6 @@ module Async =
77
open System
88
open System.Threading.Tasks
99

10-
#if !FABLE_COMPILER
11-
// Proper Async.StartImmediateAsTask implementation, without aggregate exception wrapping.
12-
let private startImmediateAsTask (computation: Async<'T>, cancellationToken) : Task<'T> =
13-
let ts = TaskCompletionSource<'T> ()
14-
Async.StartWithContinuations (
15-
computation,
16-
ts.SetResult,
17-
(function
18-
| :? AggregateException as agg -> ts.SetException agg.InnerExceptions
19-
| exn -> ts.SetException exn),
20-
(fun _ -> ts.SetCanceled ()),
21-
cancellationToken)
22-
ts.Task
23-
#endif
2410
open FSharpPlus.Extensions
2511

2612
/// <summary>Creates an async workflow from another workflow 'x', mapping its result with 'f'.</summary>
@@ -61,8 +47,8 @@ module Async =
6147
#else
6248
let map2 mapper (async1: Async<'T1>) (async2: Async<'T2>) : Async<'U> = async {
6349
let! ct = Async.CancellationToken
64-
let t1 = startImmediateAsTask (async1, ct)
65-
let t2 = startImmediateAsTask (async2, ct)
50+
let t1 = Async.AsTask (async1, ct)
51+
let t2 = Async.AsTask (async2, ct)
6652
return! Async.Await (Task.map2 mapper t1 t2) }
6753
#endif
6854

@@ -80,9 +66,9 @@ module Async =
8066
#else
8167
let map3 mapper (async1: Async<'T1>) (async2: Async<'T2>) (async3: Async<'T3>) : Async<'U> = async {
8268
let! ct = Async.CancellationToken
83-
let t1 = startImmediateAsTask (async1, ct)
84-
let t2 = startImmediateAsTask (async2, ct)
85-
let t3 = startImmediateAsTask (async3, ct)
69+
let t1 = Async.AsTask (async1, ct)
70+
let t2 = Async.AsTask (async2, ct)
71+
let t3 = Async.AsTask (async3, ct)
8672
return! Async.Await (Task.map3 mapper t1 t2 t3) }
8773
#endif
8874

src/FSharpPlus/Extensions/AsyncEnumerable.fs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@ module AsyncEnumerable =
1515
let toAsyncSeq (source: Collections.Generic.IAsyncEnumerable<_>) : SeqT<Async<_>, _> = monad.plus {
1616
let! ct = SeqT.lift Async.CancellationToken
1717
let e = source.GetAsyncEnumerator ct
18-
use _ =
19-
{ new IDisposable with
20-
member _.Dispose () =
21-
e.DisposeAsync().AsTask () |> Async.Await |> Async.RunSynchronously }
18+
use _ = { new IDisposable with member _.Dispose () = e.DisposeAsync().AsTask().Wait () }
2219

2320
let mutable currentResult = true
2421
while currentResult do

src/FSharpPlus/Extensions/Extensions.fs

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,55 @@ module Extensions =
6868

6969
#if !FABLE_COMPILER
7070

71+
/// <summary>Runs an asynchronous computation, starting immediately on the current operating system
72+
/// thread, but also returns the execution as <see cref="T:System.Threading.Tasks.Task`1"/>
73+
/// This behaves exactly like Async.StartImmediateAsTask but without unexpected exceptions-wrapping.
74+
/// </summary>
75+
///
76+
/// <remarks>If no cancellation token is provided then the default cancellation token is used.
77+
/// You may prefer using this method if you want to achive a similar behviour to async await in C# as
78+
/// async computation starts on the current thread with an ability to return a result.
79+
/// </remarks>
80+
///
81+
/// <param name="computation">The asynchronous computation to execute.</param>
82+
/// <param name="cancellationToken">The <c>CancellationToken</c> to associate with the computation.
83+
/// The default is used if this parameter is not provided.</param>
84+
///
85+
/// <returns>A <see cref="T:System.Threading.Tasks.Task"/> that will be completed
86+
/// in the corresponding state once the computation terminates (produces the result, throws exception or gets canceled)</returns>
87+
///
88+
/// <category index="0">FSharp.Core Extensions</category>
89+
///
90+
/// <example id="as-task-1">
91+
/// <code lang="fsharp">
92+
/// printfn "A"
93+
///
94+
/// let t =
95+
/// async {
96+
/// printfn "B"
97+
/// do! Async.Sleep(1000)
98+
/// printfn "C"
99+
/// } |> Async.AsTask
100+
///
101+
/// printfn "D"
102+
/// t.Wait()
103+
/// printfn "E"
104+
/// </code>
105+
/// Prints "A", "B", "D" immediately, then "C", "E" in 1 second.
106+
/// </example>
107+
static member AsTask (computation: Async<'T>, ?cancellationToken) : Task<'T> =
108+
let cancellationToken = defaultArg cancellationToken (new CancellationToken ())
109+
let ts = TaskCompletionSource<'T> ()
110+
Async.StartWithContinuations (
111+
computation,
112+
ts.SetResult,
113+
(function
114+
| :? AggregateException as agg -> ts.SetException agg.InnerExceptions
115+
| exn -> ts.SetException exn),
116+
(fun _ -> ts.SetCanceled ()),
117+
cancellationToken)
118+
ts.Task
119+
71120
// See https://github.com/fsharp/fslang-suggestions/issues/840
72121

73122
/// <summary>Return an asynchronous computation that will wait for the given task to complete and return
@@ -126,12 +175,9 @@ module Extensions =
126175

127176

128177
/// Combine all asyncs in one, chaining them in sequence order.
129-
static member Sequence (t:seq<Async<_>>) : Async<seq<_>> = async {
130-
let startImmediateAsTask ct a =
131-
Async.StartImmediateAsTask(a, ct).Result
132-
178+
static member Sequence (t: seq<Async<'T>>) : Async<seq<_>> = async {
133179
let! ct = Async.CancellationToken
134-
return t |> Seq.map (startImmediateAsTask ct) }
180+
return Seq.map (fun t -> Async.AsTask(t, ct).Result) t }
135181
#endif
136182

137183
/// Combine all asyncs in one, chaining them in sequence order.

tests/FSharpPlus.Tests/Asyncs.fs

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,12 @@ module Async =
1616
exception TestException of string
1717

1818
type Async with
19-
static member StartImmediateAsTaskCorrect (computation: Async<'T>, ?cancellationToken) : Task<'T> =
20-
let cancellationToken = defaultArg cancellationToken (new CancellationToken ())
21-
let ts = TaskCompletionSource<'T> ()
22-
Async.StartWithContinuations (
23-
computation,
24-
ts.SetResult,
25-
(function
26-
| :? AggregateException as agg -> ts.SetException agg.InnerExceptions
27-
| exn -> ts.SetException exn),
28-
(fun _ -> ts.SetCanceled ()),
29-
cancellationToken)
30-
ts.Task
31-
3219
static member AsTaskAndWait computation =
33-
let t = Async.StartImmediateAsTaskCorrect computation
20+
let t = Async.AsTask computation
3421
Task.WaitAny t |> ignore
3522
t
3623

37-
static member WhenAll (source: Async<'T> seq) = source |> Seq.map (fun x -> Async.StartImmediateAsTaskCorrect x) |> Task.WhenAll |> Async.Await
24+
static member WhenAll (source: Async<'T> seq) = source |> Seq.map (fun x -> Async.AsTask x) |> Task.WhenAll |> Async.Await
3825

3926

4027
module AsyncTests =

0 commit comments

Comments
 (0)