How can I 'resolve' Future in rust? - rust

I'm learning concurrency in rust and can't find a simple example how to return and resolve a Future.
How can I implement this javascript code in rust? It seems like I have to implement the Future trait on some struct and return it (right?), but I want a straightforward example. Instead of setTimeout it can be anything, just keep it simple.
function someAsyncTask() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("result")
}, 1_000)
})
}

The most straight forward fn() -> impl Future you can create is to simply use a async function:
use std::time::Duration;
use tokio::time;
async fn some_async_task() -> &'static str {
time::sleep(Duration::from_millis(1_000)).await;
"result"
}
If you 'remove' one layer of syntax sugar this becomes the following fn returning an async block.
use std::time::Duration;
use std::future::Future;
use tokio::time;
fn some_async_task() -> impl Future<Output = &'static str> {
async {
time::sleep(Duration::from_millis(1_000)).await;
"result"
}
}

The closest analogue in Rust futures to JavaScript's Promise constructor is the oneshot channel, as seen in futures and tokio. The “sender” of the channel is analogous to the resolve function, and the “receiver” of the channel is a Future that works like the Promise does. There is no separate reject operation; if you wish to communicate errors, you can do that by passing Result in the channel.
Translating your code:
use futures::channel::oneshot;
fn some_async_task() -> oneshot::Receiver<&'static str> {
let (sender, receiver) = oneshot::channel();
set_timeout(|| { // not a real Rust function, but if it were...
sender.send("result").unwrap()
}, 1_000);
receiver
}
Note that just like you only need new Promise in JS if you are translating something callback-like into the promise world, you only need to do this in Rust if you need to cause a future to complete from an outside event. Both should be fairly rare.

Related

Call a random function with variable arguments dynamically

I have a list of functions with variable arguments, and I want to randomly pick one of them, in runtime, and call it, on a loop. I'm looking to enhance the performance of my solution.
I have a function that calculates the arguments based on some randomness, and then (should) return a function pointer, which I could then call.
pub async fn choose_random_endpoint(
&self,
rng: ThreadRng,
endpoint_type: EndpointType,
) -> impl Future<Output = Result<std::string::String, MyError>> {
match endpoint_type {
EndpointType::Type1 => {
let endpoint_arguments = self.choose_endpoint_arguments(rng);
let endpoint = endpoint1(&self.arg1, &self.arg2, &endpoint_arguments.arg3);
endpoint
}
EndpointType::Type2 => {
let endpoint_arguments = self.choose_endpoint_arguments(rng);
let endpoint = endpoint2(
&self.arg1,
&self.arg2,
&endpoint_arguments.arg3,
rng.clone(),
);
endpoint
}
EndpointType::Type3 => {
let endpoint_arguments = self.choose_endpoint_arguments(rng);
let endpoint = endpoint3(
&self.arg1,
&self.arg2,
&endpoint_arguments.arg3,
rng.clone(),
);
endpoint
}
}
}
The error I obtain is
expected opaque type `impl Future<Output = Result<std::string::String, MyError>>` (opaque type at <src/calls/type1.rs:14:6>)
found opaque type `impl Future<Output = Result<std::string::String, MyError>>` (opaque type at <src/type2.rs:19:6>)
. The compiler advises me to await the endpoints, and this solves the issue, but is there a performance overhead to this?
Outer function:
Aassume there is a loop calling this function:
pub async fn make_call(arg1: &str, arg2: &str) -> Result<String> {
let mut rng = rand::thread_rng();
let random_endpoint_type = choose_random_endpoint_type(&mut rng);
let random_endpoint = choose_random_endpoint(&rng, random_endpoint_type);
// call the endpoint
Ok(response)
}
Now, I want to call make_call every X seconds, but I don't want my main thread to block during the endpoint calls, as those are expensive. I suppose the right way to approach this is spawning a new thread per X seconds of interval, that call make_call?
Also, performance-wise: having so many clones on the rng seems quite expensive. Is there a more performant way to do this?
The error you get is sort of unrelated to async. It's the same one you get when you try to return two different iterators from a function. Your function as written doesn't even need to be async. I'm going to remove async from it when it's not needed, but if you need async (like for implementing an async-trait) then you can add it back and it'll probably work the same.
I've reduced your code into a simpler example that has the same issue (playground):
async fn a() -> &'static str {
"a"
}
async fn b() -> &'static str {
"b"
}
fn a_or_b() -> impl Future<Output = &'static str> {
if rand::random() {
a()
} else {
b()
}
}
What you're trying to write
When you want to return a trait, but the specific type that implements that trait isn't known at compile time, you can return a trait object. Futures need to be Unpin to be awaited, so this uses a pinned box (playground).
fn a_or_b() -> Pin<Box<dyn Future<Output = &'static str>>> {
if rand::random() {
Box::pin(a())
} else {
Box::pin(b())
}
}
You may need the type to be something like Pin<Box<dyn Future<Output = &'static str> + Send + Sync + 'static>> depending on the context.
What you should write
I think the only reason you'd do the above is if you want to generate the future with some kind of async rng, then do something else, and then run the generated future after that. Otherwise there's no need to have nested futures; just await the inner futures when you call them (playground).
async fn a_or_b() -> &'static str {
if rand::random() {
a().await
} else {
b().await
}
}
This is conceptually equivalent to the Pin<Box> method, just without having to allocate a Box. Instead, you have an opaque type that implements Future itself.
Blocking
The blocking behavior of these is only slightly different. Pin<Box> will block on non-async things when you call it, while the async one will block on non-async things where you await it. This is probably mostly the random generation.
The blocking behavior of the endpoint is the same and depends on what happens inside there. It'll block or not block wherever you await either way.
If you want to have multiple make_call calls happening at the same time, you'll need to do that outside the function anyway. Using the tokio runtime, it would look something like this:
use tokio::task;
use futures::future::join_all;
let tasks: Vec<_> = (0..100).map(|_| task::spawn(make_call())).collect();
let results = join_all(tasks).await;
This also lets you do other stuff while the futures are running, in between collect(); and let results.
If something inside your function blocks, you'd want to spawn it with task::spawn_blocking (and then await that handle) so that the await call in make_call doesn't get blocked.
RNG
If your runtime is multithreaded, the ThreadRng will be an issue. You could create a type that implements Rng + Send with from_entropy, and pass that into your functions. Or you can call thread_rng or even just rand::random where you need it. This makes a new rng per thread, but will reuse them on later calls since it's a thread-local static. On the other hand, if you don't need as much randomness, you can go with a Rng + Send type from the beginning.
If your runtime isn't multithreaded, you should be able to pass &mut ThreadRng all the way through, assuming the borrow checker is smart enough. You won't be able to pass it into an async function and then spawn it, though, so you'd have to create a new one inside that function.

Sleep in Future::poll

I am trying to create a future polling for inputs from the crossterm crate, which does not provide an asynchronous API, as far as I know.
At first I tried to do something like the following :
use crossterm::event::poll as crossterm_poll;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use tokio::time::{sleep, timeout};
struct Polled {}
impl Polled {
pub fn new() -> Polled {
Polled {}
}
}
impl Future for Polled {
type Output = bool;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// If there are events pending, it returns "Ok(true)", else it returns instantly
let poll_status = crossterm_poll(Duration::from_secs(0));
if poll_status.is_ok() && poll_status.unwrap() {
return Poll::Ready(true);
}
Poll::Pending
}
}
pub async fn poll(d: Duration) -> Result<bool, ()> {
let polled = Polled::new();
match timeout(d, polled).await {
Ok(b) => Ok(b),
Err(_) => Err(()),
}
}
It technically works but obivously the program started using 100% CPU all the time since the executor always try to poll the future in case there's something new. Thus I wanted to add some asynchronous equivalent to sleep, that would delay the next time the executor tries to poll the Future, so I tried adding the following (right before returning Poll::Pending), which obviously did not work since sleep_future::poll() just returns Pending :
let mut sleep_future = sleep(Duration::from_millis(50));
tokio::pin!(sleep_future);
sleep_future.poll(cx);
cx.waker().wake_by_ref();
The fact that poll is not async forbids the use of async functions, and I'm starting to wonder if what I want to do is actually feasible, or if I'm not diving in my first problem the wrong way.
Is finding a way to do some async sleep the good way to go ?
If not, what is it ? Am I missing something in the asynchronous paradigm ?
Or is it just sometimes impossible to wrap some synchronous logic into a Future if the crate does not give you the necessary tools to do so ?
Thanks in advance anyway !
EDIT : I found a way to do what I want using an async block :
pub async fn poll(d: Duration) -> Result<bool, ()> {
let mdr = async {
loop {
let a = crossterm_poll(Duration::from_secs(0));
if a.is_ok() && a.unwrap() {
break;
}
sleep(Duration::from_millis(50)).await;
}
true
};
match timeout(d, mdr).await {
Ok(b) => Ok(b),
Err(_) => Err(()),
}
}
Is it the idiomatic way to do so ? Or did I miss something more elegant ?
Yes, using an async block is a good way to compose futures, like your custom poller and tokio's sleep.
However, if you did want to write your own Future which also invokes tokio's sleep, here's what you would need to do differently:
Don't call wake_by_ref() immediately — the sleep future will take care of that when its time comes, and that's how you avoid spinning (using 100% CPU).
You must construct the sleep() future once when you intend to sleep (not every time you're polled), then store it in your future (this will require pin-projection) and poll the same future again the next time you're polled. That's how you ensure you wait the intended amount of time and not shorter.
Async blocks are usually a much easier way to get the same result.

No method named `poll_next` found for struct `tokio::sync::mpsc::Receiver<&str>` in the current scope [duplicate]

I'm trying to learn async programming, but this very basic example doesn't work:
use std::future::Future;
fn main() {
let t = async {
println!("Hello, world!");
};
t.poll();
}
Everything I've read from the specs says this should work, but cargo complains that method "poll" can't be found in "impl std::future::Future". What am I doing wrong?
poll has this signature:
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
There are two problems with calling this in the way you do:
poll is not implement on a future Fut, but on Pin<&mut Fut>, so you need to get a pinned reference first. pin_mut! is often useful, and if the future implements Unpin, you can use Pin::new as well.
The bigger problem however is that poll takes a &mut Context<'_> argument. The context is created by the asynchronous runtime and passed in to the poll function of the outermost future. This means that you can't just poll a future like that, you need to be in an asynchronous runtime to do it.
Instead, you can use a crate like tokio or async-std to run a future in a synchronous context:
// tokio
use tokio::runtime::Runtime;
let runtime = Runtime::new().unwrap();
let result = runtime.block_on(async {
// ...
});
// async-std
let result = async_std::task::block_on(async {
// ...
})
Or even better, you can use #[tokio::main] or #[async_std::main] to convert your main function into an asynchronous function:
// tokio
#[tokio::main]
async fn main() {
// ...
}
// async-std
#[async_std::main]
async fn main() {
// ...
}

If I have nested async/await calls should I be concerned with accumulated overhead?

I'm processing data via a number of channels where each channel feeds into the next (pipeline processing). I end up with a spawn at the top that looks like this:
let future = async move {
while let Ok(msg) = r.recv().await {
forwarder.receive(msg).await;
}
};
executor_pool::ExecutorPool::spawn(future).detach();
The Forwarder looks like this:
Forwarder {
pub fn validate_sequence(&mut self, msg: TestMessage) -> Result<TestMessage, TestMessage>
pub async fn handle_action(&mut self, cmd: TestMessage);
pub async fn handle_notification(&mut self);
pub async fn receive(&mut self, cmd: TestMessage) {
match self.handle_config(cmd) {
Ok(_) => (),
Err(msg) => match self.validate_sequence(msg) {
Ok(msg) => {
self.handle_action(msg).await;
self.handle_notification().await;
},
Err(msg) => panic!("{} sequence error: expecting {} not cmd {:#?}", self.header(), self.next_seq, msg),
},
}
}
}
Both handle_action and handle_notification call into a sender which is another async fn. My concern is two-fold. The entire pathway to the send (or any other async fn) seems to require an async/await wrapper. In my case I'm 3 deep at the send. Which seems to be a bit ugly, particularly if I have to do any refactoring. Second, is there a runtime cost for each level of async/await or is the compiler smart enough to collapse these? If it helps to make this more concrete, think of it as audio processing where the first stage decodes, the next does leveling, the next does mixing, and then the final does encoding.
To expand of the refactoring concern, let's look at refactoring a for loop.
pub async fn handle_action(&mut self, cmd: FwdMessage) {
match cmd {
FwdMessage::TestData(_) => {
for sender in &self.senders {
for _ in 0 .. self.forwarding_multiplier {
sender.send(FwdMessage::TestData(self.send_count)).await.ok();
self.send_count += 1;
}
}
},
_ => panic!("unhandled action"),
}
}
Rather than a for loop, we'd like to use an iterator. However, that's going to require an async closure -- which is something I haven't figured out how to even express.
Just the act of nesting async functions does not introduce any overhead.
The async/await syntax compiles to a Future that is basically a state-machine that can execute your code in a way that it can be suspended when it needs to wait for something and resumed later. An async function and all (statically known) await'd futures are compiled together into the same Future; the function boundaries somewhat melt away. So yes, the compiler is "smart enough to collapse these" together.

How do I ensure the right HTML element is passed from JavaScript to my Rust function?

If I create a Rust function with wasm_bindgen that accepts an HtmlCanvasElement, how do I make sure it fails when it gets to the Rust side?
JavaScript:
(async () => {
const demo = await import('./pkg/demo').catch(console.error);
demo.setCanvas('Hello Element!');
})();
Rust:
use wasm_bindgen::prelude::*;
use web_sys::{console, HtmlCanvasElement};
#[wasm_bindgen]
pub fn setCanvas(canvas: &HtmlCanvasElement) {
// Why does this even get here? I didn't pass in an HtmlCanvasElement
console::log_1(&canvas);
}
It looks like I'm getting the type HtmlCanvasElement but if I try to use it as an HtmlCanvasElement, it doesn't have the functions because I'm passing in a string instead of the actual canvas element. I want it to fail when I set it, not at some later time when I try to use it.
There's not much type control at the boundary between the DOM and Rust in wasm_bindgen. The JavaScript part handles what is seen from Rust as JsValue.
It means you have to do the checked conversions yourself (or let a future lib or framework do it for you).
The JsCast trait helps for this. It lets you write this for example:
#[wasm_bindgen]
pub fn setElement(canvas: JsValue) -> Result<(), JsValue> {
match canvas.dyn_into::<HtmlCanvasElement>() {
Ok(canvas) => {
// ...
Ok(())
}
Err(_) => Err(JsValue::from_str("argument not a HtmlCanvas")),
}
}

Resources