这是使用Tokio运行返回未来的函数的示例:
use futures::sync::oneshot;
use futures::Future;
use std::thread;
use std::time::Duration;
use tokio;
#[derive(Debug)]
struct MyError {
error_code: i32,
}
impl From<oneshot::Canceled> for MyError {
fn from(_: oneshot::Canceled) -> MyError {
MyError { error_code: 1 }
}
}
fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
let (sx, rx) = oneshot::channel();
thread::spawn(move || {
thread::sleep(Duration::from_millis(100));
sx.send(100).unwrap();
});
return rx.map_err(|e| MyError::from(e));
}
fn main() {
tokio::run(deferred_task().then(|r| {
println!("{:?}", r);
Ok(())
}));
}
然而,当有问题的函数(即deferred_task
)非常重要时,代码变得复杂得多,因为?
操作似乎不容易与返回未来混合:
fn send_promise_to_worker(sx: oneshot::Sender<i32>) -> Result<(), ()> {
// Send the oneshot somewhere in a way that might fail, eg. over a channel
thread::spawn(move || {
thread::sleep(Duration::from_millis(100));
sx.send(100).unwrap();
});
Ok(())
}
fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
let (sx, rx) = oneshot::channel();
send_promise_to_worker(sx)?; // <-------- Can't do this, because the return is not a result
return rx.map_err(|e| MyError::from(e));
}
一个Future
是一个Result
,将它包装在结果中是没有意义的,它打破了impl Future
返回类型。
相反,你得到一个深度嵌套的链:
fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
let (sx, rx) = oneshot::channel();
match query_data() {
Ok(_i) => match send_promise_to_worker(sx) {
Ok(_) => Either::A(rx.map_err(|e| MyError::from(e))),
Err(_e) => Either::B(futures::failed(MyError { error_code: 2 })),
},
Err(_) => Either::B(futures::failed(MyError { error_code: 2 })),
}
}
你拥有的结果越多,嵌套越深;正是?
算子正常解决的问题。
我错过了什么吗?是否有一些语法糖可以使这更容易?
我不知道async
/ await
语法如何能够明确地帮助你使用Either
。最终,你仍然需要返回一个具体的类型,这就是Either
提供的。然而,async
/ await
将减少对Future::map
或Future::and_then
等组合器的需求。
也可以看看:
话虽这么说,你不需要在这里使用Either
。
你有连续的Result
返回函数,所以你可以从JavaScript借用一个技巧并使用IIFE来使用?
运算符。然后,我们可以将组合的Result
“提升”为未来,并将其与接收器的未来联系起来:
fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
let (tx, rx) = oneshot::channel();
let x = (|| {
let _i = query_data().map_err(|_| MyError { error_code: 1 })?;
send_promise_to_worker(tx).map_err(|_| MyError { error_code: 2 })?;
Ok(())
})();
future::result(x).and_then(|()| rx.map_err(MyError::from))
}
根据我的理解,未来,LIFE可以用try
块替换。
您也可以采用其他方式将所有内容转换为未来:
fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
let (tx, rx) = oneshot::channel();
query_data()
.map_err(|_| MyError { error_code: 1 })
.into_future()
.and_then(|_i| {
send_promise_to_worker(tx)
.map_err(|_| MyError { error_code: 2 })
.into_future()
})
.and_then(|_| rx.map_err(MyError::from))
}
这将有助于async
/ await
语法:
async fn deferred_task() -> Result<i32, MyError> {
let (tx, rx) = oneshot::channel();
query_data().map_err(|_| MyError { error_code: 1 })?;
send_promise_to_worker(tx).map_err(|_| MyError { error_code: 2 })?;
let v = await! { rx }?;
Ok(v)
}
我还看到通过在Either
特征中添加left
和right
方法来构造Future
的改进语法:
foo.left();
// vs
Either::left(foo);
但是,这不会出现在任何当前的实现中。
Future
是Result
不它不是。
有两个相关的Future
s可以谈论:
值得注意的是,Future::poll
返回的类型可以处于两种状态:
在期货箱中,“成功”和“失败”与“完整”相关联,而在标准库中则不然。在箱子里,Result
实现了IntoFuture
,在标准库中你可以使用future::ready
。这两个都允许将Result
转换为未来,但这并不意味着Result
是一个未来,只不过说Vec<u8>
是一个迭代器,即使它可以转换成一个。
有可能?
运算符(由Try
特性提供动力)将被增强为自动从Result
转换为特定类型的Future
,或者Result
甚至可以直接实现Future
,但我还没有听说过任何此类计划。
是否有一些语法糖可以使这更容易?
是的,它被称为async / await,但它还没有为广泛消费做好准备。它只在每晚支持,它使用一个略有不同的期货版本,Tokio仅通过互操作库支持,导致额外的语法开销,并且整个文档的文档仍然不稳定。
以下是一些相关链接:
What is the purpose of async/await in Rust? https://jsdw.me/posts/rust-asyncawait-preview/ https://areweasyncyet.rs/