如何并行等待多个Rust Future并在首个Err出现时终止?
解决tokio::join首个Future失败时不立即返回的问题
tokio::join!的特性是等待所有传入的Future完成后才返回结果,哪怕其中某个已经返回Err,这和你需要的「首个失败立即完成」行为不符。下面提供两种符合需求的实现方式:
方式一:用tokio::try_join!(自动取消未完成任务)
tokio::try_join!专门处理这种场景:一旦任何一个Future返回Result::Err,它会立即返回该错误,同时取消所有未完成的Future。
示例代码:
use tokio; async fn risky_task1() -> Result<(), String> { tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; Err("task1 failed early".to_string()) } async fn long_running_task2() -> Result<(), String> { tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; Ok(()) } #[tokio::main] async fn main() { match tokio::try_join!(risky_task1(), long_running_task2()) { Ok((_res1, _res2)) => println!("All tasks succeeded"), Err(err) => { println!("First error encountered: {}", err); // 此时long_running_task2已被取消,不会继续执行到完成 } } }
注意:try_join!要求所有Future的返回值都是Result类型,且错误类型需要统一(可以用Box<dyn std::error::Error>来兼容不同错误类型)。
方式二:用tokio::select!(保留未完成任务继续运行)
如果你希望首个Future失败时立即返回结果,但让其他未完成的任务继续在后台执行(和JavaScript的Promise.all行为完全一致),可以结合tokio::spawn和tokio::select!实现:
示例代码:
use tokio; async fn risky_task1() -> Result<(), String> { tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; Err("task1 failed early".to_string()) } async fn long_running_task2() -> Result<(), String> { tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; println!("task2 completed successfully"); Ok(()) } #[tokio::main] async fn main() { // 将任务放入后台任务中 let mut task1_handle = tokio::spawn(risky_task1()); let mut task2_handle = tokio::spawn(long_running_task2()); // 等待第一个完成的任务(无论成功或失败) let first_result = tokio::select! { res = &mut task1_handle => res.unwrap(), // 处理spawn的JoinError res = &mut task2_handle => res.unwrap(), }; match first_result { Err(err) => println!("First error encountered: {}", err), Ok(_) => println!("First task succeeded"), } // 如果需要等待task2完成,可取消下面的注释 // let _ = task2_handle.await; }
这种方式下,select!会在第一个任务完成时立即返回,未完成的任务会继续在后台运行直到结束。
内容的提问来源于stack exchange,提问作者programmerRaj
相关产品推荐
相关产品推荐

