3 unstable releases

0.2.1 Jun 24, 2024
0.2.0 Jun 24, 2024
0.1.0 Jun 18, 2024

#409 in Asynchronous

MIT license

20KB
367 lines

Asynchronous Deferred Calls

This crate implements the Active Object design pattern.

The entry point of this crate is Dispatcher, which wraps an Arc<Lock<T>>.

Note: Lock can be RwLock or Mutex from the async-lock crate.

Dispatcher

The Dispatcher allows you to create Callers for some methods on T.

You can then schedule deferred calls to these methods via these Callers.

Dispatcher implements Future; You must await this future for calls to actually be dispatched.

Caller

When you create a Caller for a method of T using Dispatcher, a background task is created, waiting for you to schedule deferred calls via the Caller. When a call is scheduled, the background task will lock the RwLock or Mutex and call the method on the locked T instance.

Compatible T methods

The methods must:

  • be asynchronous
  • return an implementer of ReturnType

They can take self mutably or immutably.

Note: you can use a freestanding function instead of a method if the first parameter of that function is &T or &mut T.

Summoner

if the method's last parameter is an async_channel::Sender, you can turn your Caller into a Summoner, which makes it easy to wait for a reply when you schedule a call.

Example

use async_defer::{*, async_channel::Sender, async_lock::RwLock};
use std::time::{Instant, Duration};
use std::sync::Arc;

// An example object which will be modified in deferred calls
struct Subject;

impl Subject {
    async fn print(&mut self, msg: String) -> Result<(), String> {
        println!("msg: {}", msg);
        Ok(())
    }

    async fn ping_pong(&self, payload: u8, reply: Sender<u8>) -> Result<(), String> {
        let _ = reply.send(payload).await;
        Ok(())
    }
}

let world = Arc::new(RwLock::new(Subject));
let mut dispatcher = Dispatcher::new(world);

let deferred_print = dispatcher.listen_mut_1(Subject::print);
let deferred_ping_pong = dispatcher.listen_ref_2(Subject::ping_pong);

async {
    deferred_print.call("Hello World".to_string()).await;

    let later = Instant::now() + Duration::from_secs(5);
    deferred_print.call_later(later, "Hello World".to_string()).await;

    // `ping_pong` has a reply parameter,
    // allowing us to create a `Summoner`.
    let summoner = deferred_ping_pong.summoner();
    let rep = summoner.summon(5).await;
    println!("reply: {}", rep);
};

In this example, two background tasks are created, one for each method of Subject. The ping_pong method's last parameter is a Sender, allowing us to "summon" the method.

Dependencies

~3–12MB
~137K SLoC