Rust:tokio可以被理解为类似于Javascripts事件循环,还是可以像它一样使用?

Rust:tokio可以被理解为类似于Javascripts事件循环,还是可以像它一样使用?,rust,async-await,rust-tokio,Rust,Async Await,Rust Tokio,我不确定tokio是否类似于Javascript中的事件循环,也是一个非阻塞运行时,或者它是否可以以类似的方式工作。在我看来,tokio是Rust未来的运行时。因此,它必须实现某种用户线程或任务,这可以通过事件循环(至少部分)来实现,以调度新任务 让我们看一下下面的Javascript代码: console.log('hello1'); setTimeout(() => console.log('hello2'), 0); console.log('hello3'); setTimeout

我不确定tokio是否类似于Javascript中的事件循环,也是一个非阻塞运行时,或者它是否可以以类似的方式工作。在我看来,
tokio
是Rust未来的运行时。因此,它必须实现某种用户线程或任务,这可以通过事件循环(至少部分)来实现,以调度新任务

让我们看一下下面的Javascript代码:

console.log('hello1');
setTimeout(() => console.log('hello2'), 0);
console.log('hello3');
setTimeout(() => console.log('hello4'), 0);
console.log('hello5');
输出将是

hello1
hello3
hello5
hello2
hello4
我在东京怎么做?东京总的来说是这样工作的吗?我尝试了以下代码

async fn set_timeout(f: impl Fn(), ms: u64) {
    tokio::time::sleep(tokio::time::Duration::from_millis(ms)).await;
    f()
}

#[tokio::main]
async fn main() {
    println!("hello1");
    tokio::spawn(async {set_timeout(|| println!("hello2"), 0)}).await;
    println!("hello3");
    tokio::spawn(async {set_timeout(|| println!("hello4"), 0)}).await;
    println!("hello5");
}
输出刚刚好

hello1
hello3
hello5
如果我把代码改成

    println!("hello1");
    tokio::spawn(async {set_timeout(|| println!("hello2"), 0)}.await).await;
    println!("hello3");
    tokio::spawn(async {set_timeout(|| println!("hello4"), 0)}.await).await;
    println!("hello5");
输出是

hello1
hello2
hello3
hello4
hello5

但是我没有理解整个async/await/future特性的要点,因为我的“async”set\u timeout-tasks实际上阻止了其他println语句。

与JavaScript不同,Rust在等待未来之前不会启动异步函数的执行。这意味着
set_timeout(|println!(“hello2”),0)
只会创造一个新的未来。它根本不执行它。当你等待它时,它才被执行
.wait
基本上会阻止当前线程,直到将来完成,这不是“真正的异步应用程序”。要使代码像JavaScript一样并发,可以使用宏:-

使用tokio::join; 使用东京::时间::*; 异步fn设置\u超时(f:impl fn(),ms:u64){ 睡眠(持续时间:从μm(ms))。等待; f() } #[tokio::main] 异步fn main(){ println!(“你好”); 让fut|u 1=tokio::spawn(设置|u超时(| | println!(“hello2”),0); println!(“hello3”); 让fut|u 2=tokio::spawn(设置|u超时(| | println!(“hello4”),0); println!(“hello5”); 加入!(fut_1,fut_2); } 如果您想体验一下
Promise.all
,可以使用

更多信息:-


    • 简言之:是的,Tokio的工作方式与JavaScript事件循环非常相似。但是,您的第一个代码片段有三个问题

      首先,它从
      main()。与您的JavaScript代码不同,您的JavaScript代码可能在浏览器中运行,甚至在您在控制台中键入的代码完成运行后也会运行超时,而Rust代码位于一个短暂的可执行文件中,在
      main()
      之后终止。如果可执行文件从
      main()

      第二个问题是,调用
      set\u timeout()
      async函数的匿名异步块对其返回值没有任何作用。Rust中的异步函数和JavaScript中的异步函数之间的一个重要区别是,在Rust中,您不能只调用异步函数就可以使用它。在JavaScript中,异步函数返回一个承诺,如果不等待该承诺,事件循环仍将在后台执行异步函数的代码。在Rust中,异步函数返回未来,但它与任何事件循环都没有关联,它只是为某人运行它做好准备。然后,您需要使用
      .wait
      (与JavaScript中的含义相同)等待它,或者显式地将它传递给
      tokio::spawn()
      ,以便在后台执行(与调用相同,但不等待JavaScript中的函数)。异步块两者都没有,因此调用
      set\u timeout()
      是不可行的

      最后,代码立即等待由
      spawn()
      创建的任务,这首先违背了调用
      spawn()
      的目的-
      tokio::spawn(foo())。wait
      在功能上等同于
      foo()。wait
      用于任何
      foo()

      第一个问题可以通过在
      main
      末尾添加一个小睡眠来解决。(这不是正确的修复,但将用于演示发生了什么。)第二个问题可以通过删除异步块并将
      set\u timeout()
      的返回值传递给
      tokio::spawn()
      来解决。第三个问题通过删除任务中不必要的
      .wait
      来解决

      #[tokio::main]
      async fn main() {
          println!("hello1");
          tokio::spawn(set_timeout(|| println!("hello2"), 0));
          println!("hello3");
          tokio::spawn(set_timeout(|| println!("hello4"), 0));
          println!("hello5");
          tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
      }
      

      此代码将打印“预期的”1、3、5、4、2(尽管在这样的程序中不保证顺序)。真正的代码不会以睡眠结束;相反,它将等待它创建的任务,如Shivam的回答所示。

      您必须
      等待
      main
      末尾的所有内容。Tokio不会为你做的-当
      main
      退出时,事件循环立即关闭。我在本地和操场上尝试了这个代码。同样的结果。我如何在tokio中实现Javascripts事件循环中的相同行为?Tokio就是这样工作的吗?如果你想要一个更简单的API,可以使用
      join\u all
      。谢谢,这解释了很多!