What is the best practice is to "register" the http client in one place, so it can be reused from this Elmish update function? Instead of having to create it for every request.
let update message model =
match message with
| SetMessage s -> { model with x = s }
| Loading -> { model with x = "Doing something long ..." }
let handleClick model dispatch _ =
dispatch Loading
async {
let url = Uri "https://api.github.com"
-- FIXME: too expensive to do this on per-update basis
use httpClient = new HttpClient(BaseAddress = url)
let! resp = httpClient.GetAsync "/users/srid" |> Async.AwaitTask
let! s = resp.Content.ReadAsStringAsync() |> Async.AwaitTask
dispatch (SetMessage s)
} |> Async.Start
I feel like this would normally go in Startup.fs. I use a client-only Bolero web app, so this would look like:
builder.Services.AddSingleton<HttpClient>(new HttpClient (BaseAddress=apiBase))
But then the question becomes ... how do I access it from my program in F#? What is the idiomatic way?
Probably the best way would either be to add HttpClient as another field in your model or as another parameter to your update function.
let update (client:HttpClient) message model = // Your code
let url = Uri "https://api.github.com"
let httpClient = new HttpClient(BaseAddress = url)
In general you shouldn't "do work" in your view and, by extension, event handlers. Instead, you should use the Elmish Cmd module something like this:
let update httpClient message model =
match message with
| SetMessage s ->
{ model with x = s }, Cmd.none
| GetMessageAsync ->
let cmd =
let getHttp () =
async {
let! resp = httpClient.GetAsync "/users/srid" |> Async.AwaitTask
return! resp.Content.ReadAsStringAsync() |> Async.AwaitTask
}
Cmd.OfAsync.perform getHttp () (fun s -> SetMessage s)
{ model with x = "Doing something long ..." }, cmd
let handleClick model dispatch _ =
dispatch GetMessageAsync
Related
I'm being a little adventurous with my code for the amount of experience I have with F# and I am a little worried about cross threading issues.
Background:
I have a number of orders where I need to validate the address. Some of the orders can be validated against google maps geocoding API which allows 50/ second. the rest are Australian PO Boxes which we don't have many of - but I need to validate them against a different API that only allows 1 call per second.
I have switched over most of my code from async{} functions to task{} functions and I am assuming to get something on several threads at the same time it needs to be in an async{} function or block and be piped to Async.Parallel
Question: Is this the right way to do this or will it fall over? I am wondering if I am fundamentally thinking about this the wrong way.
Notes:
I am passing a database context into the async function and updating the database within that function
I will call this from a C# ( WPF ) Application and report the progress
Am I going to have cross threading issues?
let validateOrder
(
order: artooProvider.dataContext.``dbo.OrdersEntity``,
httpClient: HttpClient,
ctx: artooProvider.dataContext,
isAuPoBox: bool
) =
async {
// Validate Address
let! addressExceptions = ValidateAddress.validateAddress (order, httpClient, ctx, isAuPoBox) |> Async.AwaitTask
// SaveExceptions
do! ctx.SubmitUpdatesAsync()
// return Exception count
return ""
}
let validateGMapOrders(httpClient: HttpClient, ctx: artooProvider.dataContext, orders: artooProvider.dataContext.``dbo.OrdersEntity`` list) =
async {
let ordersChunked = orders |> List.chunkBySize 50
for fiftyOrders in ordersChunked do
let! tasks =
fiftyOrders
|> List.map (fun (order) -> validateOrder (order, httpClient, ctx, false) )
|> Async.Parallel
do! Async.Sleep(2000)
}
let validateOrders (ctx: artooProvider.dataContext, progress: IProgress<DownloadProgressModel>) =
task {
let unvalidatedOrders =
query {
for orders in ctx.Dbo.Orders do
where (orders.IsValidated.IsNone)
select (orders)
}
|> Seq.toList
let auPoBoxOrders =
unvalidatedOrders
|> List.filter (fun order -> isAUPoBox(order) = true )
let gMapOrders =
unvalidatedOrders
|> List.filter (fun order -> isAUPoBox(order) = false )
let googleHttpClient = new HttpClient()
let auspostHttpclient = Auspost.AuspostApi.getApiClient ()
// Google maps validations
do! validateGMapOrders(googleHttpClient,ctx,gMapOrders)
// PO Box Validations
for position in 0 .. auPoBoxOrders.Length - 1 do
let! result = validateOrder (gMapOrders[position], auspostHttpclient, ctx, true)
do! Task.Delay(1000)
return true
}
When I have had to deal with rate-limited API problems I hide that API behind a MailboxProcessor that maintains an internal time to comply with the rate limit but appears as a normal async API from the outside.
Since you have two API's with different rate limits I'd parameterise the time delay and processing action then create one object for each API.
open System
type Request = string
type Response = string
type RateLimitedProcessor() =
// Initialise 1s in past so ready to start immediately.
let mutable lastCall = DateTime.Now - TimeSpan(0, 0, 1)
let mbox = new MailboxProcessor<Request * AsyncReplyChannel<Response>>((fun mbox ->
let rec f () =
async {
let! (req, reply) = mbox.Receive()
let msSinceCall = (DateTime.Now - lastCall).Milliseconds
// wait 1s between requests
if msSinceCall < 1000 then
do! Async.Sleep (1000 - msSinceCall)
lastCall <- DateTime.Now
reply.Reply "Response"
// Call self recursively to process the next incoming message
return! f()
}
f()
))
do mbox.Start()
member __.Process(req:Request): Async<Response> =
async {
return! mbox.PostAndAsyncReply(fun reply -> req, reply)
}
interface IDisposable with
member this.Dispose() = (mbox :> IDisposable).Dispose()
I want to write a simple load balancer for some requests coming into a C# web api app.
(I only use the C# stuff as a convenient way to create a web server).
Whats the best way to approach this? (I havent really done any mailbox stuff in F#)
If I were to use mailboxes/agents...then I post the request as a message, fine...but how do I get the response back to web api request handler?
Isnt it all fire and forget? (I have, ironically, done some erlang)
(I CAN have a simple mutable global index of which is the next worker service to handle the request...but this is my opportunity to do it nicely).
actually I think I may have done something very similar to this in erlang, and I think the initiator would pass a return address where to send the message back (and the return address was the process id of the initiator), it would then wait for the response, and when it gets it (or times out), it would then do whatever it needed to do.
Is that a sensible mechanism in F#?
------------------------ edit ------------------------
So, https://www.codemag.com/Article/1707051/Writing-Concurrent-Programs-Using-F
describes a similar set up and it seems I need to use, and actually this works,
but it ISNT quite the same mechanism as my Erlang suggestion about.
Here each client sends a PostAndReply, and then waits for the response before replying back.....that seems unnecessary, ideally the reply would go all the way back to the origin, and the intermediaries would fire and forget in between.
open System
type Message = string * AsyncReplyChannel<string>
[<EntryPoint>]
let main argv =
let myFirstAgent =
MailboxProcessor<Message>.Start(fun inbox ->
let rec loop () =
async {
let! (message, replyChannel) = inbox.Receive()
replyChannel.Reply (String.Format ("1. Received message: {0}", message))
do! loop ()
}
loop ())
let mySecondAgent =
MailboxProcessor<Message>.Start(fun inbox ->
let rec loop () =
async {
let! (message, replyChannel) = inbox.Receive()
replyChannel.Reply (String.Format ("2. Received message: {0}", message))
do! loop ()
}
loop ())
let agents = [ myFirstAgent; mySecondAgent ]
let replyAgent =
MailboxProcessor<Message>.Start(fun inbox ->
let rec loop index =
async {
let! (message, replyChannel) = inbox.Receive()
let reply = (agents.Item index).PostAndReply(fun rc -> message,rc)
replyChannel.Reply reply
do! loop ((index + 1) % 2)
}
loop 0)
let foo = replyAgent.PostAndReply(fun rc -> "Hello", rc)
let foo1 = replyAgent.PostAndReply(fun rc -> "Hello", rc)
let foo2 = replyAgent.PostAndReply(fun rc -> "Hello", rc)
let foo3 = replyAgent.PostAndReply(fun rc -> "Hello", rc)
let foo4 = replyAgent.PostAndReply(fun rc -> "Hello", rc)
//myFirstAgent.Post "Hello!"
printfn "Hello World from F#!"
System.Console.ReadLine() |> ignore
0 // return an integer exit code
D'oh, what I need to do is actually UNDERSTAND the example, rather than just hack together code!
if the reply agent just forwards it...then we're done.
let replyAgent =
MailboxProcessor<Message>.Start(fun inbox ->
let rec loop index =
async {
let! (message, replyChannel) = inbox.Receive()
let reply = (agents.Item index).Post(message, replyChannel)
do! loop ((index + 1) % 2)
}
loop 0)
I've created a website using WebSharper and has stumbled into a problem. I wish to integrate the site with VSTS REST API. To do that (seemlessly) I need to forward a session cookie. How do I do that in an WebSharper-Ajax call. My current implementation of the Ajax call prior to needing this looks like this and works just fine for the other needs I've had so far
let Ajax (request : Request) =
let httpMethod = request.Method
let url = request.EndPoint
let data = request.AsJson
let success ok =
System.Action<obj,string,JqXHR>(
fun res _ _ ->
let result = (res :?> string |> Json.Parse)
if JS.HasOwnProperty result "error" then
{
ErrorType = result?error
Reason = result?reason
} |> pushError
else
result
|> Success
|> ok
)
let contentType = Union<bool,string>.Union2Of2("application/json")
try
Async.FromContinuations
<| fun (ok, ko, _) ->
let settings = JQuery.AjaxSettings(
Url = url,
DataType = JQuery.DataType.Text,
Type = As<JQuery.RequestType> httpMethod,
Success = success ok,
ContentType = contentType,
Error = System.Action<JqXHR,string,string>(fun jqXHR _ _ ->
let error =
jqXHR?responseText
|> Json.Parse
{
ErrorType = error?error
Reason = error?reason
} |> pushError |> ok
)
)
match data with
Some data ->
settings.Data <- data
| None -> ()
JQuery.Ajax(settings) |> ignore
with e ->
async {
return {
ErrorType ="uncaught exception";
Reason = e.Message
} |> Error
}
It turns out that the solution is pretty easy. After creating the AjaxSetting object, simply use dynamic typing to add the xhrFields object
settings?xhrFields <- obj()
settings?xhrFields?withCredentials <- true
How to do an simple await in F# ?
In C# I have code like this:
await collection.InsertOneAsync(DO);
var r = collection.ReplaceOneAsync((fun d -> d.Id = DO.Id), DO)
So I created a let await = ... to my F# code become more similar with my C# code.
My current F# code is this:
let awaits (t: Threading.Tasks.Task) = t |> Async.AwaitTask |> Async.RunSynchronously
let await (t: Threading.Tasks.Task<'T>) = t |> Async.AwaitTask |> Async.RunSynchronously
let Busca (numero) =
let c = collection.Find(fun d -> d.Numero=numero).ToList()
c
let Insere(DO: DiarioOficial) =
//collection.InsertOneAsync(DO) |> Async.AwaitTask |> Async.RunSynchronously
collection.InsertOneAsync(DO) |> awaits
let Salva (DO: DiarioOficial) =
//let r = collection.ReplaceOneAsync((fun d -> d.Id = DO.Id), DO) |> Async.AwaitTask |> Async.RunSynchronously
let r = collection.ReplaceOneAsync((fun d -> d.Id = DO.Id), DO) |> await
r
I want to have only one definition for await (awaits), but the best I could do is this, because on Insere, type is Task, but on Salva, type is Task<'T>
If i use only the await, I get this compile error:
FS0001 The type 'Threading.Tasks.Task' is not compatible with the type 'Threading.Tasks.Task<'a>'
If I use only the awaits, it compiles, but I lose the return type from the async Task
I want to merge the await and awaits in a single
let await = ...
How can I do this?
In F# we tend to use another syntax. It is described e.g. here: https://fsharpforfunandprofit.com/posts/concurrency-async-and-parallel/.
or here: https://learn.microsoft.com/en-us/dotnet/fsharp/tutorials/asynchronous-and-concurrent-programming/async
The idea of working with C# Tasks is to "convert" them to async with Async.Await<'T>
You can do it probably another way, but it is the most straightforward.
There are two parts of writing async code in both F# and C#.
You need to mark the method or code block as asynchronous. In C#, this is done using the async keyword. The F# equivalent is to use the async { ... } block (which is an expression, but otherwise, it is similar).
Inside async method or async { .. } block, you can make non-blocking calls. In C#, this is done using await and in F# it is done using let!. Note that this is not just a function call - the compiler handles this in a special way.
F# also uses Async<T> type rather than Task<T>, but those are easy to convert - e.g. using Async.AwaitTask. So, you probably want something like this:
let myAsyncFunction () = async {
let! _ = collection.InsertOneAsync(DO) |> Async.AwaitTask
let r = collection.ReplaceOneAsync((fun d -> d.Id = DO.Id), DO)
// More code goes here
}
I used let! to show the idea, but if you have an asynchronous operation that returns unit, you can also use do!
do! collection.InsertOneAsync(DO) |> Async.AwaitTask
Considering the following
type MyClass () =
member x.ReadStreamAsync(stream:Stream) =
async {
let tcs = new TaskCompletionSource<int>()
let buffer = Array.create 2048 0uy
let! bytesReadCount = stream.ReadAsync(buffer, 0, buffer.Length) |> Async.AwaitTask
if bytesReadCount > 0 then
for i in 0..bytesReadCount do
if buffer.[i] = 10uy then
tcs.SetResult(i)
// Omitted more code to handle the case if 10uy is not found..
return tcs.Task
}
The code reads from a stream until in meets a certain character (represented by a byte value) at which point the task returned by the method completes.
The function signature of DoSomethingAsync is unit -> Async<Task<int>>, but I would like it to be unit -> Task<int> such that it can be used more generally in .NET.
Can this be done in F# using an asynchronous expression, or do I can to rely more on the Task constructs of .NET?
Given that you don't actually use the async workflow for anything in your example, the easiest solution would be to forgo it entirely:
member x.DoSomethingAsync() =
let tcs = new TaskCompletionSource<int>()
Task.Delay(100).Wait()
tcs.SetResult(10)
tcs.Task
This implementation of DoSomethingAsync has the type unit -> Task<int>.
It's not clear to me exactly what you're trying to do, but why don't you just do the following?
member x.DoSomethingAsync() =
async {
do! Async.Sleep 100
return 10 } |> Async.StartAsTask
This implementation also has the type unit -> Task<int>.
Based on the updated question, here's a way to do it:
member x.DoSomethingAsync(stream:Stream) =
async {
let buffer = Array.create 2048 0uy
let! bytesReadCount =
stream.ReadAsync(buffer, 0, buffer.Length) |> Async.AwaitTask
if bytesReadCount > 0
then
let res =
[0..bytesReadCount]
|> List.tryFind (fun i -> buffer.[i] = 10uy)
return defaultArg res -1
else return -1
}
|> Async.StartAsTask
The DoSomethingAsync function has the type Stream -> System.Task<int>. I didn't know what to do in the else case, so I just put -1, but I'm sure you can replace it with something more correct.