Is it possible to wait (without blocking) on an Async<'t> within an Akka.Net actor computation? I want to achieve something similar to the following.
actor {
let! msg = mailbox.Receive()
match msg with
| Foo ->
let! x = async.Return "testing 123" // Some async function, return just an example
() // Do something with result
}
The verb 'await' must have an object - for example, 'I am awaiting your answer'. And the object of 'await' is normally inanimate, not a person, and often abstract. So you can't say, 'John was awaiting me'. The verb 'wait' can come in different structures.
to wait for or be waiting for something: He's anxiously awaiting his test results. A marvellous reception awaited me on my first day at work. The long/eagerly awaited sequel is now available online.
1a : to wait for We are awaiting his arrival. await a decision. b : to remain in abeyance until a treaty awaiting ratification. 2 : to be in store for He wonders what awaits him next. 3 obsolete : to lie in wait for.
No you can't use async
/ await
or any variant thereof inside an actor's mailbox and get safe results.
Each actor maintains its own context, which includes important details like the Sender of the previous message and other important pieces of state which might change. Actors process messages serially, so as soon as the call inside its mailbox finishes it immediately begins processing its next message - if you put an await call inside the mailbox, the actor will be processing a totally different message than the one you started with by the time your await call returns.
A better pattern for leveraging asynchronous calls and the TAP inside actors is to use the PipeTo pattern. It looks like we don't have any documentation for this yet at http://akkadotnet.github.io/, so I'll give you a real-world code sample (in C#):
public void Handle(ExplicitReplyOperation<CampaignsForAppRequest> message)
{
Context.IncrementMessagesReceived();
_loaderActor.Ask(message.Data).ContinueWith(r =>
{
var campaigns = (IList<Campaign>)r.Result;
message.Originator.Tell(new CampaignsForAppResponse()
{
AppId = message.Data.AppId,
ActiveCampaigns = campaigns
}, ActorRef.NoSender);
return campaigns;
}).PipeTo(Self);
}
In this sample I have a TypedActor
that continues a Task, does some post-processing, and then uses the PipeTo
operator (an Akka.NET extension method you can apply to any Task
object) to pipe the results of the task into this actor's mailbox once the operation is finished. That way I can close over any of the state I need and my actor can continue processing messages in a safe way while this async operation continues.
This now seems to be possible!
let system = ConfigurationFactory.Default() |> System.create "FSharpActors"
let asyncActor =
spawn system "MyActor"
<| fun mailbox ->
let rec loop() =
actor {
let! name = mailbox.Receive()
Akka.Dispatch.ActorTaskScheduler.RunTask(fun () ->
async {
printfn "Hello %s" name
do! Async.Sleep 5000
} |> Async.StartAsTask :> Threading.Tasks.Task)
return! loop()
}
loop()
asyncActor <! "Alice"
asyncActor <! "Bob"
asyncActor <! "Eve"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With