3

Does rust currently have a library implement function similar to JavaScript's setTimeout and setInverval?, that is, a library that can call multiple setTimeout and setInterval to implement management of multiple tasks at the same time.

I feel that tokio is not particularly convenient to use. I imagine it to be used like this:

fn callback1() {
    println!("callback1");
}

fn callback2() {
    println!("callback2");
}

set_interval(callback1, 10);
set_interval(callback1, 20);
set_timeout(callback1, 30);

Of course, I can simulate a function to make it work:

// just for test, not what I wanted at all
type rust_listener_callback = fn();

fn set_interval(func: rust_listener_callback, duration: i32) {
    func()
}

fn set_timeout(func: rust_listener_callback,  duration: i32) {
    func();
}

If a set_interval is implemented in this way, multiple combinations, dynamic addition and deletion, and cancellation are not particularly convenient:

use tokio::time;

async fn set_interval(func: rust_listener_callback, duration: u64) {
    let mut interval = time::interval(Duration::from_millis(duration));
    tokio::spawn(async move {
        loop {
            interval.tick().await;
            func()
        }
    }).await;
}

// emm maybe loop can be removed, just a sample

While, What I want to know is if there is a library to do this, instead of writing it myself.

I have some idea if I would write it myself. Generally, all functions are turned into a task queue or task tree, and then tokio::time::delay_for can be used to execute them one by one, but the details are actually more complicated.

However, I think that this general capability may have already been implemented but I has not found for the time being, so I want to ask here, Thank you very much.

And importantly, I hope it can support single thread

Ondrej Slinták
  • 31,386
  • 20
  • 94
  • 126
聂小涛
  • 503
  • 3
  • 16
  • What's the issue with putting `delay_for` at the beginning of the async code you want to delay? – Boiethios Feb 20 '20 at 15:53
  • @Boiethios emmm, thank you but I don't know if you understand all my intentions, here is more than just a delay – 聂小涛 Feb 20 '20 at 15:59
  • That's for the `setTimeout` part, at least. – Boiethios Feb 20 '20 at 16:05
  • Your using `Interval` wrong: It's a stream, so it has [many convenience methods](https://docs.rs/tokio/0.2.11/tokio/stream/trait.StreamExt.html) to handle "multiple combinations, dynamic addition and deletion, and cancellation". – mcarton Feb 20 '20 at 16:15
  • @mcarton Maybe I don't know much about stream, but I think that using methods of stream to manage tasks is not very convenient. I looked at the implementation of some related parts of [deno](https://github.com/denoland/deno) and it seems that these methods are not used. is there further information or examples? and thank you – 聂小涛 Feb 20 '20 at 16:42

3 Answers3

1

setTimeout can be done like this without the need for a crate:

tokio::spawn(async move {
    tokio::time::sleep(Duration::from_secs(5)).await;
    // code goes here

});
m1212e
  • 303
  • 4
  • 8
0

I asked myself the same question a few days ago, created a solution for this (for tokio runtimes), and found your stackoverflow post just now.

https://crates.io/crates/tokio-js-set-interval

code.rs

use std::time::Duration;
use tokio_js_set_interval::{set_interval, set_timeout};
#[tokio::main]
async fn main() {
    println!("hello1");
    set_timeout!(println!("hello2"), 0);
    println!("hello3");
    set_timeout!(println!("hello4"), 0);
    println!("hello5");
    // give enough time before tokios runtime exits
    tokio::time::sleep(Duration::from_millis(1)).await;
}

But this must be used with caution. There is no guarantee that the futures will be executed (because tokios runtime must run long enough). Use it only:

  • for educational purposes,
  • and if you have low priority background tasks that you don't expect to get executed always
phip1611
  • 5,460
  • 4
  • 30
  • 57
0

I created a library just for this which allows setting many timeouts using only 1 tokio task (instead of spawning a new task for each timeout) which provides better performance and lower memory usage.

The library also supports cancelling timeouts, and provides some ways to optimize the performance of the timeouts.

Check it out:

https://crates.io/crates/set_timeout

Usage example:

#[tokio::main]
async fn main() {
    let scheduler = TimeoutScheduler::new(None);
    
    // schedule a future which will run after 1.234 seconds from now.
    scheduler.set_timeout(Duration::from_secs_f32(1.234), async move {
        println!("It works!");
    });

    // make sure that the main task doesn't end before the timeout is executed, because if the main
    // task returns the runtime stops running.
    tokio::time::sleep(Duration::from_secs(2)).await;
}
Babayaga
  • 1
  • 1