Skip to main content

spawn

Function spawn 

pub fn spawn(future: impl Future<Output = ()> + 'static) -> TaskHandle
Expand description

Create a new async background task.

This function allows creating a new async task in which Godot signals can be awaited, like it is possible in GDScript. If a reference to Self is not flexible enough regarding lifetimes, your function calling spawn() can capture the state in a Gd<Self> pointer instead. The TaskHandle that is returned provides synchronous introspection into the current state of the task.

Signals can be converted to futures in the following ways:

Signal typeSimple futureFallible future (handles freed object)
UntypedSignal::to_future()Signal::to_fallible_future()
TypedTypedSignal::to_future()TypedSignal::to_fallible_future()

§Panics

If called from any other thread than the main thread.

§Engine shutdown

If the awaited signal’s object is freed before the signal fires during engine teardown, the task does not resume: it stays suspended and is dropped when the runtime shuts down, without panicking. If the signal does fire (e.g. tree_exiting, emitted while the node is still alive), the task resumes as usual – but by then the engine may have freed the node and any other captured Gd<T>, so accessing one follows the usual liveness rules (panic with safeguards, UB when disengaged).

§Examples

An example using timers:

#[derive(GodotClass)]
#[class(init, base=Node)]
struct Game {
    base: Base<Node>,
}

#[godot_api]
impl Game {
    // Async sleep using Godot timers. Takes `Gd<Self>` by value, so no `&self`/`&mut self`
    // reference is held while the task is suspended at the await point.
    async fn sleep(this: Gd<Self>, duration: f64) {
        // To access object, use short-lived bind/bind_mut -> guard dropped after statement.
        this.bind().do_something();

        // Godot APIs don't need to go through bind/bind_mut at all.
        let timer = this.get_tree().create_timer(duration);

        // Await without holding any borrow on `this`. Keeping a bind()/bind_mut() guard
        // across an await point is problematic: while suspended, other access to the
        // object, for example through process(&mut self), will panic.
        timer.signals().timeout().to_future().await;
    }

    fn show_messages(&mut self) {
        // Obtain Gd<Self>, since the closure cannot capture `&mut self` due to lifetimes.
        // If this method is linked to a signal, consider using a #[func(gd_self)] parameter.
        let this = self.to_gd();

        // spawn() polls the future up to the first .await point, during which `&mut self` is
        // still held. base_mut() yields a guard that allows re-borrowing `self`, so the bind()
        // inside sleep() doesn't panic with "already bound".
        let _guard = self.base_mut();

        godot::task::spawn(async move {
            godot_print!("Start!");
            Self::sleep(this, 1.0).await;
            godot_print!("One second later!");
        });
    }
}

With typed signals:

#[derive(GodotClass)]
#[class(init)]
struct Building {
   base: Base<RefCounted>,
}

#[godot_api]
impl Building {
   #[signal]
   fn constructed(seconds: u32);
}

let house = Building::new_gd();
godot::task::spawn(async move {
    println!("Wait for construction...");

    // Emitted arguments can be fetched in tuple form.
    // If the signal has no parameters, you can skip `let` and just await the future.
    let (seconds,) = house.signals().constructed().to_future().await;

    println!("Construction complete after {seconds}s.");
});

With untyped signals:

let node = Node::new_alloc();
let signal = Signal::from_object_signal(&node, "signal");

godot::task::spawn(async move {
    println!("Starting task...");

    // Explicit generic arguments needed, here `()`:
    signal.to_future::<()>().await;

    println!("Node has changed: {}", node.get_name());
});