I found a variety of SO questions on this but couldn't figure out an F# solution. I need to block wait for an event to fire at me to check the data it returns. I am using Rx to receive event 3 times:
let disposable =
Observable.take 3 ackNack
|> Observable.subscribe (
fun (sender, data) ->
Console.WriteLine("{0}", data.AckNack)
Assert.True(data.TotalAckCount > 0u)
)
I would like to either turn results into a list, so they can be checked later on by the test framework (xUnit), or wait for all 3 events to complete and pass the Assert.True.
How would I wait for 3 events to fire before continuing? I can see there's an Observable.wait other sources suggest Async.RunSynchronously.
I think the easiest option is to use Async.AwaitObservable function - sadly, this is not yet available in the F# core library, but you can get it from the FSharpx.Async package, or just copy the function soruce from GitHub.
Using the function, you should be able to write something like:
let _, data =
Observable.take 3 ackNack
|> Async.AwaitObservable
|> Async.RunSynchronously
Console.WriteLine("{0}", data.AckNack)
Assert.True(data.TotalAckCount > 0u)
Related
I've spent hours combing through documentation and tutorials, but can't figure out how to use ReactiveX to poll an external resource, or anything for that matter, every at an interval. Below is some code I wrote to get information from a REST API at an interval.
open System
open System.Reactive.Linq
module MyObservable =
let getResources =
async {
use client = new HttpClient()
let! response = client.GetStringAsync("http://localhost:8080/items") |> Async.AwaitTask
return response
} |> Async.StartAsTask
let getObservable (interval: TimeSpan) =
let f () = getResources.Result
Observable.Interval(interval)
|> Observable.map(fun _ -> f ())
To test this out, I tried subscribing to the Observable and waiting five seconds. It does receive something every second for five seconds, but the getResources is only called the first time and then the result is just used at each interval. How can I modify this to make the REST call at each interval instead of just the result of the first call being used over and over again?
let mutable res = Seq.empty
getObservable (new TimeSpan(0,0,1))
|> Observable.subscribe(fun (x: seq<string>) -> res <- res |> Seq.append x;)
|> ignore
Threading.Thread.Sleep(5000)
Don't use a Task. Tasks are what we call "hot", meaning that if you have a value of type Task in your hand, it means that the task is already running, and there is nothing you can do about it. In particular, this means you cannot restart it, or start a second instance of it. Once a Task is created, it's too late.
In your particular case it means that getResources is not "a way to start a task", but just "a task". Already started, already running.
If you want to start a new task every time, you have two alternatives:
First (the worse alternative), you could make getResources a function rather than a value, which you can do by giving it a parameter:
let getResources () =
async { ...
And then call it with that parameter:
let f () = getResources().Result
This will run the getResources function afresh every time you call f(), which will create a new Task every time and start it.
Second (a better option), don't use a Task at all. You're creating a perfectly good async computation and then turning it into a Task only to block on getting its result. Why? You can block on an async's result just as well!
let getResources = async { ... }
let getObservable interval =
let f () = getResources |> Async.RunSynchronously
...
This works, even though getResources is not a function, because asyncs, unlike Tasks, are what we call "cold". This means that, if you have an async in your hand, it doesn't mean that it's already running. async, unlike Task, represents not an "already running" computation, but rather "a way to start a computation". A corollary is that you can start it multiple times from the same async value.
One way to start it is via Async.RunSynchronously as I'm doing in my example above. This is not the best way, because it blocks the current thread until the computation is done, but it's equivalent to what you were doing with accessing the Task.Result property, which also blocks until the Task is done.
I'm trying to scrape some websites that need to run their JavaScript before the document has all the data I'm interested in. I'm trying to open a WebBrowser and wait for the document to load, but I can't get the data when I try to switch back to the thread the WebBrowser is on. Trying to run it without switching back to the thread gives casting errors. = (
What's stopping the async from switching threads? How do I fix this problem?
Script
open System
open System.Windows.Forms
open System.Threading
let step a = do printfn "%A" a
let downloadWebSite (address : Uri) (cont : HtmlDocument -> 'a) =
let browser = new WebBrowser()
let ctx = SynchronizationContext.Current
browser.DocumentCompleted.Add (fun _ ->
printfn "Document Loaded" )
async {
do step 1
do browser.Navigate(address)
do step 2
let! _ = Async.AwaitEvent browser.DocumentCompleted
do step 3
do! Async.SwitchToContext ctx
do step 4
return cont browser.Document }
let test =
downloadWebSite (Uri "http://www.google.com") Some
|> Async.RunSynchronously
Output
>
1
2
Document Loaded
3
# It just hangs here. I have to manually interrupt fsi.
- Interrupt
>
4
The problem with your approach is that RunSynchronously blocks the thread that you are trying to use to run the rest of the asynchronous computation using Async.SwitchToContext ctx.
When using F# Interactive, there is one main thread which runs in the F# Interactive and handles the user interactions. This is the thread that can use Windows Forms controls, so you correctly create WebBrowser outside of async. The waiting for DocumentCompleted happens on a thread pool thread (which runs the async workflow), but when you try to switch back to the main thread, it is already blocked by Async.RunSynchronously.
You can avoid blocking the thread by running a loop that calls Application.DoEvents to process events on the main thread (which will also allow it to run the rest of your async). Your downloadWebSite stays the same, but now you wait using:
let test =
downloadWebSite (Uri "http://www.google.com") Some
|> Async.Ignore
|> Async.StartAsTask
while not test.IsCompleted do
System.Threading.Thread.Sleep(100)
System.Windows.Forms.Application.DoEvents()
This is a bit of a hack - and there might be a better way of structuring this if you do not really need to wait for the result (e.g. just return a task and wait before running the next command), but this should do the trick.
I am trying to get the below code to work in a F# async workflow, but I am getting the error "Unexpected symbol '}' in expression". I am fairly new to both F# and async in general. What am I missing here.
let someFunction (req : HttpRequestMesssage) a b =
// code
async{
let! readToProvider =
req.Content.ReadAsMultipartAsync(provider)
|> Async.AwaitIAsyncResult
} |> Async.RunSynchronously
req.CreateResponse(HttpStatusCode.OK)
I worry that my previous answer wasn't quite what you want. What I supplied just got you through the compile error. But one thing about it, is that it does not run asynchronously. Task.Wait and Async.RunSynchronously will both block the running thread until the operation is complete.
If you want to actually be async, i.e. not blocking, you have to put the entire method, or at least the last part of it, into the async block, such that you're actually returning an async op to the caller. So the answer would be
let someFunction (req : HttpRequestMesssage) a b =
async {
let! readToProvider = (req.Content.ReadAsMultipartAsync provider) |> Async.AwaitIAsyncResult
return req.CreateResponse HttpStatusCode.OK
}
This option returns not the response, but an Async<Response>. So now the caller can decide how to run it, either blocking or truly asynchronously.
This way if you're using a web server that handles asynchronous requests, then you can simply connect this function to an endpoint (probably converting the Async to a Task at the point of connection, since most .net async web servers are written from C# perspective) and it'll run asynchronously without blocking a thread. Or if you're calling it from another async op you can do do! someFunction ... and it'll run asynchronously. But if the caller doesn't care and just wants to run synchronously, it can do someFunction ... |> Async.RunSynchronously. So you get more flexibility there. And you can always define let someFunctionSync ... = someFunction ... |> Async.RunSynchronously if that's the more common use case.
I'd recommend going this way unless you really want to enforce blocking.
You're doing it right. You're only getting the error because you're ending your async block with a let! expression. Change it to return!, or do! ... |> Async.Ignore and you'll be good.
Blocks in F# (neither workflows nor regular code blocks) should not end with let.
Of course if all you're really doing in the workflow is that one call, you don't need the workflow block at all (you never really should need to write a block for a single call). Just do
req.Content.ReadAsMultipartAsync provider
|> Async.AwaitIAsyncResult
|> Async.Ignore
|> Async.RunSynchronously
req.CreateResponse HttpStatusCode.OK
Or for that matter, just use the built in Tasks Wait, which does the same thing as Async.RunSynchronously:
(req.Content.ReadAsMultipartAsync provider).Wait()
I have a COM object, which I connect to, and I should recieve an event, which would confirm that connection is established. I write code and test it in F# interactive, and for some reason it wouldn't catch COM events when I use Async.RunSynchronously.
/// This class wraps COM event into F# Async-compatible event
type EikonWatcher(eikon : EikonDesktopDataAPI) =
let changed = new Event<_>()
do eikon.add_OnStatusChanged (fun e -> changed.Trigger true)
member self.StatusChanged = changed.Publish
/// My method
let ``will that connection work?`` () =
let eikon = EikonDesktopDataAPIClass() :> EikonDesktopDataAPI // create COM object
let a = async {
let watcher = EikonWatcher eikon // wrap it
eikon.Initialize() |> ignore // send connection request
let! result = Async.AwaitEvent watcher.StatusChanged // waiting event
printfn "%A" result // printing result
return result
}
// I use either first or second line of code, not both of them
Async.Start (Async.Ignore a) // does not hang, result prints
Async.RunSynchronously (Async.Ignore) a // hangs!!!
/// Running
``will that connection work?`` ()
At the same time, code works perfectly well with RunSynchronously when I insert it into console app.
What should I do so that to prevent that nasty behavior?
The code we write under within a single Thread (as in STA) feels like it is made of independant pieces each having their own life, but this is actually a fallacy : everything is mediated under a common event loop, which "linearizes" the various calls.
So everything we do, unless explicitely spoecified otherwise, is essentially single threaded and you can not wait for yourself without creating a deadlock.
When you specify Async.Start it does start a new, independant computation which runs on its own, a "thread".
Whereas When you call runsynchronously, it awaits on the same 'thread'.
Now if the event you are waiting, which feels like an independant thing, is actually 'linearized' by the same event loop, you are actually waiting for yourself, hence the deadlock.
Something useful if you want to wait "asynchronously", (aka wait for an event, but not actually block and leave the opportunity for any other task to perform work) you can use the following code within your async block :
async {
....
let! token = myAsyncTask |> Async.StartChild
let! result = token
....
}
I am doing F# programming, I have some special requirements.
I have 3 class instances; each class instance has to run for one hour every day, from 9:00AM to 10:00AM. I want to control them from main program, starting them at the same time, and stop them also at the same time. The following is my code to start them at the same time, but I don’t know how to stop them at the same time.
#light
module Program
open ClassA
open ClassB
open ClassC
let A = new CalssA.A("A")
let B = new ClassB.B("B")
let C = new ClassC.C("C")
let task = [ async { return A.jobA("A")};
async { return B.jobB("B")};
async { return C.jobC("C")} ]
task |> Async.Parallel |> Async.RunSynchronously |> ignore
Anyone knows hows to stop all 3 class instances at 10:00AM, please show me your code.
Someone told me that I can use async with cancellation tokens, but since I am calling instance of classes in different modules, it is difficult for me to find suitable code samples.
Thanks,
The jobs themselves need to be stoppable, either by having a Stop() API of some sort, or cooperatively being cancellable via CancellationTokens or whatnot, unless you're just talking about some job that spins in a loop and you'll just thread-abort it eventually? Need more info about what "stop" means in this context.
As Brian said, the jobs themselves need to support cancellation. The programming model for cancellation that works the best with F# is based on CancellationToken, because F# keeps CancellationToken automatically in asynchronous workflows.
To implement the cancellation, your JobA methods will need to take additional argument:
type A() =
member x.Foo(str, cancellationToken:CancellationToken) =
for i in 0 .. 10 do
cancellationToken.ThrowIfCancellationRequested()
someOtherWork()
The idea is that you call ThrowIfCancellationRequested frequently during the execution of your job. If a cancellation is requested, the method thorws and the operation will stop. Once you do this, you can write asynchronous workflow that gets the current CancellationToken and passes it to JobA member when calling it:
let task =
[ async { let! tok = Async.CancellationToken
return A.JobA("A", tok) };
async { let! tok = Async.CancellationToken
return B.JobB("B") }; ]
Now you can create a new token using CancellationTokenSource and start the workflow. When you then cancel the token source, it will automatically stop any jobs running as part of the workflow:
let src = new CancellationTokenSource()
Async.Start(task, cancellationToken = src.Token)
// To cancel the job:
src.Cancel()
You asked this question on hubfs.net, and I'll repeat here my answer: try using Quartz.NET. You'd just implement IInteruptableJob in A,B,C, defining how they stop. Then another job at 10:00AM to stop the others.
Quartz.NET has a nice tutorial, FAQ, and lots of examples. It's pretty easy to use for simple cases like this, yet very powerful if you ever need more complex scheduling, monitoring jobs, logging, etc.