zng_app::handler

Macro async_app_hn

source
macro_rules! async_app_hn {
    ($($tt:tt)+) => { ... };
}
Expand description

Declare an async clone-move app event handler.

The macro input is a closure with optional clone-move variables, internally it uses async_clmv_fn! so the input is the same syntax.

The handler generates a future for each event, the future is polled immediately if it does not finish it is scheduled to update in on_pre_update or on_update depending on if the handler was assigned to a preview event or not.

Note that this means propagation can only be meaningfully stopped before the first .await, after, the event has already propagated.

§Examples

The example declares an async event handler for the CLICK_EVENT.

CLICK_EVENT.on_event(async_app_hn!(|_, _| {
    println!("Clicked Somewhere!");

    task::run(async {
        println!("In other thread!");
    }).await;

    println!("Back in UI thread, in an app update.");
})).perm();

The closure input is A, Box<dyn AppWeakHandle> for all handlers and A is ClickArgs for this example. Note that if you want to use the event args you must annotate the input type, the context and handle types are inferred.

The handle can be used to unsubscribe the event handler, if unsubscribe is called the handler will be dropped some time before the next event update. Running tasks are not canceled by unsubscribing, the only way to cancel then is by returning early inside the async blocks.

CLICK_EVENT.on_event(async_app_hn!(|args: ClickArgs, handle| {
    println!("Clicked {}!", args.target);
    task::run(async move {
        handle.unsubscribe();
    });
})).perm();

Internally the async_clmv_fn! macro is used so you can clone-move variables into the handler.

let status = var("pending..".to_txt());

CLICK_EVENT.on_event(async_app_hn!(status, |args: ClickArgs, _| {
    status.set(formatx!("processing {}..", args.target));

    task::run(async move {
        println!("do something slow");
    }).await;

    status.set(formatx!("finished {}", args.target));
})).perm();

// can still use after:
let text = status;

In the example above only a clone of status is moved into the handler. Note that handlers always capture by move, if status was not listed in the clone-move section it would not be available after the handler is created. See async_clmv_fn! for details.

§Futures and Clone-Move

You may want to always clone-move captures for async handlers, because they then automatically get cloned again for each event. This needs to happen because you can have more then one handler task running at the same type, and both want access to the captured variables.

This second cloning can be avoided by using the async_hn_once! macro instead, but only if you expect a single event.