|
| 1 | +# Use with F# and Visual Basic |
| 2 | + |
| 3 | +Asynchronous methods in the Polly.Core API return either `ValueTask` or `ValueTask<T>` |
| 4 | +instead of `Task` or `Task<T>`. This is because Polly v8 was designed to be optimized |
| 5 | +for high performance and uses `ValueTask` to avoid unnecessary allocations. |
| 6 | + |
| 7 | +One downside to this choice is that in Visual Basic and F#, it is not possible to directly |
| 8 | +await a method that returns `ValueTask` or `ValueTask<T>`, instead requiring the use of |
| 9 | +`Task` and `Task<T>`. |
| 10 | + |
| 11 | +A proposal to support awaiting `ValueTask` can be found in F# language design repository: |
| 12 | +[[RFC FS-1021 Discussion] Support Interop with ValueTask in Async Type][fsharp-fslang-design-118]. |
| 13 | + |
| 14 | +To work around this limitation, you can use the [`AsTask()`][valuetask-astask] method to convert a |
| 15 | +`ValueTask` to a `Task` in F# and Visual Basic. This does however introduce an allocation and make |
| 16 | +the code a bit more difficult to work with compared to C#. |
| 17 | + |
| 18 | +Examples of such conversions are shown below. |
| 19 | + |
| 20 | +## F\# |
| 21 | + |
| 22 | +```fsharp |
| 23 | +open FSharp.Control |
| 24 | +open System |
| 25 | +open System.Threading |
| 26 | +open System.Threading.Tasks |
| 27 | +open IcedTasks |
| 28 | +open Polly |
| 29 | +
|
| 30 | +let getBestFilmAsync token = |
| 31 | + task { |
| 32 | + do! Task.Delay(1000, token) |
| 33 | + return "https://www.imdb.com/title/tt0080684/" |
| 34 | + } |
| 35 | +
|
| 36 | +let demo () = |
| 37 | + task { |
| 38 | + // The ResiliencePipelineBuilder creates a ResiliencePipeline |
| 39 | + // that can be executed synchronously or asynchronously |
| 40 | + // and for both void and result-returning user-callbacks. |
| 41 | + let pipeline = |
| 42 | + ResiliencePipelineBuilder() |
| 43 | + .AddTimeout(TimeSpan.FromSeconds(5)) |
| 44 | + .Build() |
| 45 | +
|
| 46 | + let token = CancellationToken.None |
| 47 | +
|
| 48 | + // Synchronously |
| 49 | + pipeline.Execute(fun () -> printfn "Hello, world!") |
| 50 | +
|
| 51 | + // Asynchronously |
| 52 | + // Note that Polly expects a ValueTask to be returned, so the function uses the valueTask builder |
| 53 | + // from IcedTasks to make it easier to use ValueTask. See https://github.com/TheAngryByrd/IcedTasks. |
| 54 | + do! pipeline.ExecuteAsync( |
| 55 | + fun token -> |
| 56 | + valueTask { |
| 57 | + printfn "Hello, world! Waiting for 2 seconds..." |
| 58 | + do! Task.Delay(1000, token) |
| 59 | + printfn "Wait complete." |
| 60 | + } |
| 61 | + , token |
| 62 | + ) |
| 63 | +
|
| 64 | + // Synchronously with result |
| 65 | + let someResult = pipeline.Execute(fun token -> "some-result") |
| 66 | +
|
| 67 | + // Asynchronously with result |
| 68 | + // Note that Polly expects a ValueTask<T> to be returned, so the function uses the valueTask builder |
| 69 | + // from IcedTasks to make it easier to use ValueTask<T>. See https://github.com/TheAngryByrd/IcedTasks. |
| 70 | + let! bestFilm = pipeline.ExecuteAsync( |
| 71 | + fun token -> |
| 72 | + valueTask { |
| 73 | + let! url = getBestFilmAsync(token) |
| 74 | + return url |
| 75 | + } |
| 76 | + , token |
| 77 | + ) |
| 78 | +
|
| 79 | + printfn $"Link to the best film: {bestFilm}" |
| 80 | + } |
| 81 | +``` |
| 82 | + |
| 83 | +[Source][sample-fsharp] |
| 84 | + |
| 85 | +## Visual Basic |
| 86 | + |
| 87 | +```vb |
| 88 | +Imports System.Threading |
| 89 | +Imports Polly |
| 90 | + |
| 91 | +Module Program |
| 92 | + Sub Main() |
| 93 | + Demo().Wait() |
| 94 | + End Sub |
| 95 | + |
| 96 | + Async Function Demo() As Task |
| 97 | + ' The ResiliencePipelineBuilder creates a ResiliencePipeline |
| 98 | + ' that can be executed synchronously or asynchronously |
| 99 | + ' and for both void and result-returning user-callbacks. |
| 100 | + Dim pipeline = New ResiliencePipelineBuilder().AddTimeout(TimeSpan.FromSeconds(5)).Build() |
| 101 | + |
| 102 | + ' Synchronously |
| 103 | + pipeline.Execute(Sub() |
| 104 | + Console.WriteLine("Hello, world!") |
| 105 | + End Sub) |
| 106 | + |
| 107 | + ' Asynchronously |
| 108 | + ' Note that the function is wrapped in a ValueTask for Polly to use as VB.NET cannot |
| 109 | + ' await ValueTask directly, and AsTask() is used to convert the ValueTask returned by |
| 110 | + ' ExecuteAsync() to a Task so it can be awaited. |
| 111 | + Await pipeline.ExecuteAsync(Function(token) |
| 112 | + Return New ValueTask(GreetAndWaitAsync(token)) |
| 113 | + End Function, |
| 114 | + CancellationToken.None).AsTask() |
| 115 | + |
| 116 | + ' Synchronously with result |
| 117 | + Dim someResult = pipeline.Execute(Function(token) |
| 118 | + Return "some-result" |
| 119 | + End Function) |
| 120 | + |
| 121 | + ' Asynchronously with result |
| 122 | + ' Note that the function is wrapped in a ValueTask(Of String) for Polly to use as VB.NET cannot |
| 123 | + ' await ValueTask directly, and AsTask() is used to convert the ValueTask(Of String) returned by |
| 124 | + ' ExecuteAsync() to a Task(Of String) so it can be awaited. |
| 125 | + Dim bestFilm = Await pipeline.ExecuteAsync(Function(token) |
| 126 | + Return New ValueTask(Of String)(GetBestFilmAsync(token)) |
| 127 | + End Function, |
| 128 | + CancellationToken.None).AsTask() |
| 129 | + |
| 130 | + Console.WriteLine("Link to the best film: {0}", bestFilm) |
| 131 | + |
| 132 | + End Function |
| 133 | + |
| 134 | + Async Function GreetAndWaitAsync(token As CancellationToken) As Task |
| 135 | + Console.WriteLine("Hello, world! Waiting for 1 second...") |
| 136 | + Await Task.Delay(1000, token) |
| 137 | + End Function |
| 138 | + |
| 139 | + Async Function GetBestFilmAsync(token As CancellationToken) As Task(Of String) |
| 140 | + Await Task.Delay(1000, token) |
| 141 | + Return "https://www.imdb.com/title/tt0080684/" |
| 142 | + End Function |
| 143 | +End Module |
| 144 | +``` |
| 145 | + |
| 146 | +[Source][sample-vb] |
| 147 | + |
| 148 | +[fsharp-fslang-design-118]: https://github.com/fsharp/fslang-design/discussions/118 |
| 149 | +[valuetask-astask]: https://learn.microsoft.com/dotnet/api/system.threading.tasks.valuetask.astask |
| 150 | +[sample-fsharp]: https://github.com/App-vNext/Polly/tree/main/samples/Intro.FSharp |
| 151 | +[sample-vb]: https://github.com/App-vNext/Polly/tree/main/samples/Intro.VisualBasic |
0 commit comments