'How to spawn a repeating task and await its first execution?

Re-building setInterval in JS:

pub async fn set_interval<T, F>(interval: Duration, do_something: T)
where
    T: (Fn() -> F) + Send + Sync + 'static,
    F: Future + Send,
{
    let forever = task::spawn(async move {
        let mut interval = time::interval(interval);

        loop {
            interval.tick().await;
            do_something().await;
        }
    });

    forever.await;
}

This works, but I want it to be .awaitable until the first execution ends.

I.e. Instead of doing:

do_something().await
set_interval(Duration::from_secs(1), do_something)

I want to:

set_interval(Duration::from_secs(1), do_something).await

Note that this is different behaviour from above in that it runs the do_something task immediately, but it's intended.

My solution:

pub async fn set_interval<T, F>(interval: Duration, do_something: T)
where
    T: (Fn() -> F) + Send + Sync + 'static,
    F: Future + Send,
{
    do_something().await;
    
    task::spawn(async move {
        let forever = task::spawn(async move {
            let mut interval = time::interval(interval);

            loop {
                interval.tick().await;
                do_something().await;
            }
        });

        forever.await;
    });
}

This works, but I have some doubts

  1. Is it a waste of resources to task::spawn twice here? Any way it can be done only once?
  2. Is it a mistake to discard the outer task::spawn as soon as the function ends? Will it keep getting run indefinitely, even though it's not tracked anymore, just fired and forgotten?


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source