How do you make an infinite loop in Rust and make it run with a delay?

Hey all, for my Rust app I want to make a loop which runs infinitely every second. loop in Rust runs infinitely but I do not know how to set this to run every few seconds.

1 Like

The simple answer is to use std::thread::sleep.

use std::thread::sleep;
use std::time::{Duration, Instant};

let interval = Duration::from_secs(1);
let mut next_time = Instant::now() + interval;
loop {
    do_something();
    sleep(next_time - Instant::now());
    next_time += interval;
}

This is an example of how to do it that “keeps good time”; if you just sleep(interval); then it will still work, but drift “slow” because of scheduling jitter and because of the time taken by the rest of the loop.

However, using sleep is often the wrong solution in situations such as async or web environments. It's fine if you are writing your own main() top-level loop or in a thread you created, but otherwise, you may need to do something else, such as tokio::time::sleep if you're using tokio, or set_interval on the web.

6 Likes

Could you maybe further explain why it could be bad for web enviroments?

Because sleeping blocks the executing thread. If you sleep(5), then your webserver won't be able to use that particular thread for serving requests for 5 seconds. (If you environment is single-threaded, it will straight up freeze the application for that amount of time.)

This, of course, isn't specific to the web. It's equally bad for a GUI, for a real-time low-level application, and basically anything non-trivial.

What you are probably trying to do is set up a timer and a callback that is fired periodically. If you use the appropriate crates, they will let the program and the OS do other, useful things while sleeping, instead of busy-waiting.

If you sleep(5), then your webserver won't be able to use that particular thread for serving requests for 5 seconds.

Sorry, I was unclear; by “on the web” I meant in a web browser in WASM. The same principle applies, but the negative consequence is freezing the UI event loop for that page, so the user can't interact with the page.

So I should look into using tokio for when I might use it on web?

Tokio is an async runtime which does provide functions for asynchronous sleeping. But it's not necessarily what you must use. For example, if you are writing a GUI, it's likely that the GUI library has some custom facility for doing this. What is your actual use-case?

I will want to try it out on the web to read and use data for calculations in Rust, since I heard Rust is best at that and not JS. And the JS side might still want to read mouse input etc. meanwhile.
Would it also be viable to use it every f.e. 10ms, which is quite fast.

I see Rust also has requestAnimationFrame. And Tokio has setInterval. Which one would be recommended for refreshing every few ms. I could also just use rAF with a timer to limit it.

If your application is in a web page and interactive then requestAnimationFrame is a good option.

If requestAnimationFrame's behavior of not running if the page is not visible is undesirable, check out

which is a convenient wrapper around setTimeout and setInterval.

Don't try to use Tokio's scheduling from web-WASM code — it will not work. (You can use the channels and synchronization in Tokio, tokio::sync, anywhere, but most of Tokio is intended for an environment where it controls the event loop (is acting as the executor). That's never true in a web browser.)

1 Like

I see gloo timers has settimout, but not setinterval. I stumbled upon this: setInterval which seems like its made, but I don't see the option for it.

That'd be Interval::new, (similarly to Timeout::new).

1 Like

thanks :slight_smile: Do you reckon having setinterval in Rust is equally or even better optimized than setinerval in JS? I could possibly use either/or.

gloo_timers calls JavaScript APIs, so there's no difference in runtime performance, just ergonomics.

2 Likes

I see thanks ^^. I don't understand how it works, exactly. But can I not use this:
#[wasm_bindgen(js_name = "setInterval", catch)]
fn set_interval(handler: &Function, timeout: i32) -> Result<i32, JsValue>;
(from your link) directly? Nvm, I see there more to it. But size wise, would it not be better to copy code from crates? (If you can work the code out that is, of course).

And than there is only one question left as well: might it still be better to use requestAnimationFrame and limit that to a certain amount of ms, instead of setting setInterval to that amount of ms? But I'm guessing it's also using the JS API so it won't matter.

It's important to keep in mind when using WASM that it can't actually "do" anything, in the sense of talk to the browser, to put things on screen, get input, or in this case, go to sleep. The only things it can do is take input, think, and give output. Everything else is just asking JavaScript to do it for it.

Unless you're just taking values, thinking about them for a relevant amount of time, then returning values, Rust isn't really any better than JavaScript at doing things in the browser. It's best suited for dealing with big amounts of data, for example images.

There are people doing cool things to make an entire webpage with Rust (like Yew), but from what I've seen, nothing that I would consider a good idea to use for a job.

1 Like

Oh. You're right yes. I feel like I tend to 'overcomplicate' what it does. Thanks for pointing that out.

Do note that direct access to the DOM and other underlying browser functionality is being worked on - that should make this sort of thing a lot simpler and more like working on a traditional operating system.

I sure hope so. But I'm afraid by the time I get more hang of all this stuff, that will be pushed and made available.. But I suppose it's never bad to have a better understanding.

I see that this sadly only takes integers.. And I will need to use floats, like you can in JS.

setInterval takes timeout in milliseconds. Are you sure that you need sub-millisecond waits?

2 Likes