是否可以遍历Vec
,调用一个方法,在每个方法上返回Future
,并构建一个Future
链,由(最终)由消费者?是否执行后Future
s取决于Future
中早期Vec
的结果。
澄清:
我正在处理一个可以从任意一组上游源获取数据的应用程序。
请求数据将依次检查每个来源。如果第一个来源有错误(Err
),或者没有可用的数据(None
),则会尝试第二个来源,依此类推。
每个来源应该只尝试一次,并且在所有来源返回结果之前不应尝试任何来源。记录错误,但忽略,将查询传递给下一个上游数据源。
我有一些工作代码用于获取元数据:
/// Attempts to read/write data to various external sources. These are
/// nested types, because a data source may exist as both a reader and a writer
struct StoreManager {
/// Upstream data sources
readers: Vec<Rc<RefCell<StoreRead>>>,
/// Downstream data sinks
writers: Vec<Rc<RefCell<StoreWrite>>>,
}
impl StoreRead for StoreManager {
fn metadata(self: &Self, id: &Identifier) -> Box<Future<Option<Metadata>, Error>> {
Box::new(ok(self.readers
.iter()
.map(|store| {
executor::block_on(store.borrow().metadata(id)).unwrap_or_else(|err| {
error!("Error on metadata(): {:?}", err);
None
})
})
.find(Option::is_some)
.unwrap_or(None)))
}
}
除了对所有Box
和Rc/RefCell
废话的不满之外,我真正关心的是executor::block_on()
电话。它会阻塞,等待每个Future
返回结果,然后继续下一个。
鉴于可以调用fn_returning_future().or_else(|_| other_fn())
等等,是否有可能建立这样的动态链?或者是否需要在移动到下一个迭代器之前对迭代器中的每个Future
进行全面评估?
答案 0 :(得分:3)
您可以使用stream::unfold
将单个值转换为流。在这种情况下,我们可以使用filebeat.prospectors:
- paths:
- /var/log/nginx/virus123.log
input_type: log
fields:
type:virus123
json.keys_under_root: true
- paths:
- /var/log/nginx/virus1234.log
input_type: log
fields:
type:virus1234
json.keys_under_root: true
setup.template.name: "filebeat-%{[beat.version]}"
setup.template.pattern: "filebeat-%{[beat.version]}-*"
setup.template.overwrite: true
processors:
- drop_fields:
fields: ["beat","source"]
output.elasticsearch:
index: index: "filebeat-%{[beat.version]}-%{[fields.type]:other}-%{+yyyy.MM.dd}"
hosts: ["http://127.0.0.1:9200"]
迭代器作为单个值。
IntoIter
extern crate futures; // 0.2.1
use futures::{executor, future, stream, Future, FutureExt, Stream, StreamExt};
type Error = ();
fn network_request(val: i32) -> impl Future<Item = i32, Error = Error> {
// Just for demonstration, don't do this in a real program
use std::{thread, time::{Duration, Instant}};
thread::sleep(Duration::from_secs(1));
println!("Resolving {} at {:?}", val, Instant::now());
future::ok(val * 100)
}
fn requests_in_sequence(vals: Vec<i32>) -> impl Stream<Item = i32, Error = Error> {
stream::unfold(vals.into_iter(), |mut vals| {
match vals.next() {
Some(v) => network_request(v).map(|v| Some((v, vals))).left_future(),
None => future::ok(None).right_future(),
}
})
}
fn main() {
let s = requests_in_sequence(vec![1, 2, 3]);
let f = s.for_each(|v| {
println!("-> {}", v);
Ok(())
});
executor::block_on(f).unwrap();
}
要忽略Resolving 1 at Instant { tv_sec: 3416957, tv_nsec: 29270595 }
-> 100
Resolving 2 at Instant { tv_sec: 3416958, tv_nsec: 29450854 }
-> 200
Resolving 3 at Instant { tv_sec: 3416959, tv_nsec: 29624479 }
-> 300
和Err
,您必须将None
移至Error
,使Item
输入Item
:
Result<Option<T>, Error>
extern crate futures; // 0.2.1
use futures::{executor, future, stream, Future, FutureExt, Never, Stream, StreamExt};
struct Error;
fn network_request(val: i32) -> impl Future<Item = Option<i32>, Error = Error> {
// Just for demonstration, don't do this in a real program
use std::{thread, time::{Duration, Instant}};
thread::sleep(Duration::from_millis(100));
println!("Resolving {} at {:?}", val, Instant::now());
match val {
1 => future::err(Error), // An error
2 => future::ok(None), // No data
_ => future::ok(Some(val * 100)), // Success
}
}
fn requests_in_sequence<I>(vals: I) -> impl Stream<Item = Result<Option<i32>, Error>, Error = Never>
where
I: IntoIterator<Item = i32>,
{
stream::unfold(vals.into_iter(), |mut vals| {
match vals.next() {
Some(v) => {
network_request(v)
.then(|v| future::ok(Some((v, vals)))) // Convert `Item` into `Result<Option<i32>, Error>`
.left_future()
}
None => future::ok(None).right_future(),
}
})
}
fn main() {
let reqs = requests_in_sequence(vec![1, 2, 3, 4, 5]);
let success = reqs
.filter_map(|x| future::ok(x.ok())) // Ignore all `Result::Err`
.filter_map(|x| future::ok(x)) // Ignore all `Option::None`
.next(); // Get first value
match executor::block_on(success) {
Ok((Some(v), _s)) => println!("First success: {}", v),
Ok((None, _s)) => println!("No successful requests"),
Err(_) => unreachable!("Impossible to fail"),
}
}
是否可以建立像这样的动态链
是的,但它涉及大量的额外分配和间接,并且要求至少有一个值可以访问:
Resolving 1 at Instant { tv_sec: 3428278, tv_nsec: 513758474 }
Resolving 2 at Instant { tv_sec: 3428278, tv_nsec: 614059691 }
Resolving 3 at Instant { tv_sec: 3428278, tv_nsec: 714256066 }
First success: 300
另见:
是否需要在移动到下一个之前完全评估迭代器中的每个
fn requests_in_sequence(vals: Vec<i32>) -> Box<Future<Item = i32, Error = Error>> { let mut vals = vals.into_iter(); let val1 = vals.next().expect("Need at least one value to start from"); vals.fold(Box::new(network_request(val1)), |acc, val| { Box::new(acc.or_else(move |_| network_request(val))) }) }
不是您自己的要求的一部分吗?强调我的:
请求数据将检查每个来源,依次。如果第一个来源出现错误(
Future
),或者没有可用数据(Err
),则会尝试第二个来源