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
Copy file name to clipboardExpand all lines: README.md
+85-12Lines changed: 85 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# IcedTasks
2
2
3
-
## What
3
+
## What is IcedTasks?
4
4
5
5
This library contains additional [computation expressions](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/computation-expressions) for the [task CE](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/task-expressions) utilizing the [Resumable Code](https://github.com/fsharp/fslang-design/blob/main/FSharp-6.0/FS-1087-resumable-code.md) introduced [in F# 6.0](https://devblogs.microsoft.com/dotnet/whats-new-in-fsharp-6/#making-f-faster-and-more-interopable-with-task).
6
6
@@ -14,16 +14,21 @@ This library contains additional [computation expressions](https://docs.microsof
14
14
15
15
-`ParallelAsync<'T>` - Utilizes the [applicative syntax](https://docs.microsoft.com/en-us/dotnet/fsharp/whats-new/fsharp-50#applicative-computation-expressions) to allow parallel execution of [Async<'T> expressions](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/async-expressions). See [this discussion](https://github.com/dotnet/fsharp/discussions/11043) as to why this is a separate computation expression.
16
16
17
+
-`AsyncEx<'T>` - Slight variation of F# async semantics described further below with examples.
- <sup>2</sup> - Which [Nuget](https://www.nuget.org/) package do they come from
@@ -35,11 +40,71 @@ This library contains additional [computation expressions](https://docs.microsof
35
40
- <sup>8</sup> - `CancellationToken` is propagated to all types the support implicit `CancellatationToken` passing. Calling `cancellableTask { ... }` nested inside `async { ... }` (or any of those combinations) will use the `CancellationToken` from when the code was started.
36
41
- <sup>9</sup> - Cancellation will be checked before binds and runs.
37
42
- <sup>10</sup> - Allows parallel execution of the asynchronous code using the [Applicative Syntax](https://docs.microsoft.com/en-us/dotnet/fsharp/whats-new/fsharp-50#applicative-computation-expressions) in computation expressions.
43
+
- <sup>11</sup> - Allows `use` of `IAsyncDisposable` with the computation expression. See [IAsyncDisposable](https://docs.microsoft.com/en-us/dotnet/api/system.iasyncdisposable) for more info.
44
+
45
+
## Why should I use this?
46
+
47
+
48
+
### AsyncEx
49
+
50
+
AsyncEx is similar to Async except in the following ways:
51
+
52
+
1. Allows `use` for [IAsyncDisposable](https://docs.microsoft.com/en-us/dotnet/api/system.iasyncdisposable)
53
+
54
+
```fsharp
55
+
open IcedTasks
56
+
let fakeDisposable = { new IAsyncDisposable with member __.DisposeAsync() = ValueTask.CompletedTask }
57
+
58
+
let myAsyncEx = asyncEx {
59
+
use! _ = fakeDisposable
60
+
return 42
61
+
}
62
+
````
63
+
2. Allows `let!/do!` against Tasks/ValueTasks/[any Awaitable](https://devblogs.microsoft.com/pfxteam/await-anything/)
38
64
65
+
```fsharp
66
+
open IcedTasks
67
+
let myAsyncEx = asyncEx {
68
+
let! _ = task { return 42 } // Task<T>
69
+
let! _ = valueTask { return 42 } // ValueTask<T>
70
+
let! _ = Task.Yield() // YieldAwaitable
71
+
return 42
72
+
}
73
+
```
74
+
3. When Tasks throw exceptions they will use the behavior described in [Async.Await overload (esp. AwaitTask without throwing AggregateException](https://github.com/fsharp/fslang-suggestions/issues/840)
75
+
76
+
77
+
```fsharp
78
+
let data = "lol"
79
+
80
+
let inner = asyncEx {
81
+
do!
82
+
task {
83
+
do! Task.Yield()
84
+
raise (ArgumentException "foo")
85
+
return data
86
+
}
87
+
:> Task
88
+
}
89
+
90
+
let outer = asyncEx {
91
+
try
92
+
do! inner
93
+
return ()
94
+
with
95
+
| :? ArgumentException ->
96
+
// Should be this exception and not AggregationException
97
+
return ()
98
+
| ex ->
99
+
return raise (Exception("Should not throw this type of exception", ex))
100
+
}
101
+
```
102
+
103
+
104
+
### For [ValueTasks](https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask/)
39
105
40
-
## How
106
+
- F# doesn't currently have a `valueTask` computation expression. [Until this PR is merged.](https://github.com/dotnet/fsharp/pull/14755)
41
107
42
-
### ValueTask
43
108
44
109
```fsharp
45
110
open IcedTasks
@@ -50,6 +115,12 @@ let myValueTask = task {
50
115
}
51
116
```
52
117
118
+
### For Cold & CancellableTasks
119
+
- You want control over when your tasks are started
120
+
- You want to be able to re-run these executable tasks
121
+
- You don't want to pollute your methods/functions with extra CancellationToken parameters
122
+
- You want the computation to handle checking cancellation before every bind.
123
+
53
124
54
125
### ColdTask
55
126
@@ -125,6 +196,8 @@ let executeWriting = task {
125
196
126
197
### ParallelAsync
127
198
199
+
- When you want to execute multiple asyncs in parallel and wait for all of them to complete.
Copy file name to clipboardExpand all lines: docsSrc/index.md
+165Lines changed: 165 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,19 +13,184 @@ This library contains additional [computation expressions](https://docs.microsof
13
13
14
14
-`ParallelAsync<'T>` - Utilizes the [applicative syntax](https://docs.microsoft.com/en-us/dotnet/fsharp/whats-new/fsharp-50#applicative-computation-expressions) to allow parallel execution of [Async<'T> expressions](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/async-expressions). See [this discussion](https://github.com/dotnet/fsharp/discussions/11043) as to why this is a separate computation expression.
15
15
16
+
-`AsyncEx<'T>` - Slight variation of F# async semantics described further below with examples.
17
+
16
18
## Why should I use IcedTasks?
17
19
20
+
### AsyncEx
21
+
22
+
AsyncEx is similar to Async except in the following ways:
23
+
24
+
1. Allows `use` for [IAsyncDisposable](https://docs.microsoft.com/en-us/dotnet/api/system.iasyncdisposable)
25
+
26
+
```fsharp
27
+
open IcedTasks
28
+
let fakeDisposable = { new IAsyncDisposable with member __.DisposeAsync() = ValueTask.CompletedTask }
29
+
30
+
let myAsyncEx = asyncEx {
31
+
use! _ = fakeDisposable
32
+
return 42
33
+
}
34
+
````
35
+
2. Allows `let!/do!` against Tasks/ValueTasks/[any Awaitable](https://devblogs.microsoft.com/pfxteam/await-anything/)
36
+
37
+
```fsharp
38
+
open IcedTasks
39
+
let myAsyncEx = asyncEx {
40
+
let! _ = task { return 42 } // Task<T>
41
+
let! _ = valueTask { return 42 } // ValueTask<T>
42
+
let! _ = Task.Yield() // YieldAwaitable
43
+
return 42
44
+
}
45
+
```
46
+
3. When Tasks throw exceptions they will use the behavior described in [Async.Await overload (esp. AwaitTask without throwing AggregateException](https://github.com/fsharp/fslang-suggestions/issues/840)
47
+
48
+
49
+
```fsharp
50
+
let data = "lol"
51
+
52
+
let inner = asyncEx {
53
+
do!
54
+
task {
55
+
do! Task.Yield()
56
+
raise (ArgumentException "foo")
57
+
return data
58
+
}
59
+
:> Task
60
+
}
61
+
62
+
let outer = asyncEx {
63
+
try
64
+
do! inner
65
+
return ()
66
+
with
67
+
| :? ArgumentException ->
68
+
// Should be this exception and not AggregationException
69
+
return ()
70
+
| ex ->
71
+
return raise (Exception("Should not throw this type of exception", ex))
72
+
}
73
+
```
74
+
75
+
18
76
### For [ValueTasks](https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask/)
19
77
20
78
- F# doesn't currently have a `valueTask` computation expression. [Until this PR is merged.](https://github.com/dotnet/fsharp/pull/14755)
21
79
80
+
81
+
```fsharp
82
+
open IcedTasks
83
+
84
+
let myValueTask = task {
85
+
let! theAnswer = valueTask { return 42 }
86
+
return theAnswer
87
+
}
88
+
```
89
+
22
90
### For Cold & CancellableTasks
23
91
- You want control over when your tasks are started
24
92
- You want to be able to re-run these executable tasks
25
93
- You don't want to pollute your methods/functions with extra CancellationToken parameters
26
94
- You want the computation to handle checking cancellation before every bind.
27
95
28
96
97
+
### ColdTask
98
+
99
+
Short example:
100
+
101
+
```fsharp
102
+
open IcedTasks
103
+
104
+
let coldTask_dont_start_immediately = task {
105
+
let mutable someValue = null
106
+
let fooColdTask = coldTask { someValue <- 42 }
107
+
do! Async.Sleep(100)
108
+
// ColdTasks will not execute until they are called, similar to how Async works
109
+
Expect.equal someValue null ""
110
+
// Calling fooColdTask will start to execute it
111
+
do! fooColdTask ()
112
+
Expect.equal someValue 42 ""
113
+
}
114
+
115
+
```
116
+
117
+
### CancellableTask & CancellableValueTask
118
+
119
+
The examples show `cancellableTask` but `cancellableValueTask` can be swapped in.
120
+
121
+
Accessing the context's CancellationToken:
122
+
123
+
1. Binding against `CancellationToken -> Task<_>`
124
+
125
+
```fsharp
126
+
let writeJunkToFile =
127
+
let path = Path.GetTempFileName()
128
+
129
+
cancellableTask {
130
+
let junk = Array.zeroCreate bufferSize
131
+
use file = File.Create(path)
132
+
133
+
for i = 1 to manyIterations do
134
+
// You can do! directly against a function with the signature of `CancellationToken -> Task<_>` to access the context's `CancellationToken`. This is slightly more performant.
135
+
do! fun ct -> file.WriteAsync(junk, 0, junk.Length, ct)
136
+
}
137
+
```
138
+
139
+
2. Binding against `CancellableTask.getCancellationToken`
140
+
141
+
```fsharp
142
+
let writeJunkToFile =
143
+
let path = Path.GetTempFileName()
144
+
145
+
cancellableTask {
146
+
let junk = Array.zeroCreate bufferSize
147
+
use file = File.Create(path)
148
+
// You can bind against `CancellableTask.getCancellationToken` to get the current context's `CancellationToken`.
149
+
let! ct = CancellableTask.getCancellationToken ()
150
+
for i = 1 to manyIterations do
151
+
do! file.WriteAsync(junk, 0, junk.Length, ct)
152
+
}
153
+
```
154
+
155
+
Short example:
156
+
157
+
```fsharp
158
+
let executeWriting = task {
159
+
// CancellableTask is an alias for `CancellationToken -> Task<_>` so we'll need to pass in a `CancellationToken`.
160
+
// For this example we'll use a `CancellationTokenSource` but if you were using something like ASP.NET, passing in `httpContext.RequestAborted` would be appropriate.
161
+
use cts = new CancellationTokenSource()
162
+
// call writeJunkToFile from our previous example
163
+
do! writeJunkToFile cts.Token
164
+
}
165
+
166
+
167
+
```
168
+
169
+
### ParallelAsync
170
+
171
+
- When you want to execute multiple asyncs in parallel and wait for all of them to complete.
0 commit comments