Как ждать все порожденные асинхронные задачи - PullRequest
3 голосов
/ 22 июня 2019

У меня есть асинхронный метод, который использует tokio::fs для исследования каталога:

use failure::Error;
use futures::Future;
use std::path::PathBuf;
use tokio::prelude::*;

fn visit_async(path: PathBuf) -> Box<Future<Item = (), Error = Error> + Send> {
    let task = tokio::fs::read_dir(path)
        .flatten_stream()
        .for_each(move |entry| {
            let path = entry.path();
            if path.is_dir() {
                let task = visit_async(entry.path());
                tokio::spawn(task.map_err(drop));
            } else {
                println!("File: {:?}", path);
            }
            future::ok(())
        })
        .map_err(Error::from);
    Box::new(task)
}

Мне нужно выполнить другое будущее после того, как закончится все будущее, возвращаемое этим методом, а также все задачи, созданные им. Есть ли лучший способ, чтобы просто запустить другую среду выполнения?

let t = visit_async(PathBuf::from(".")).map_err(drop);
tokio::run(t);

tokio::run(future::ok(()));

1 Ответ

0 голосов
/ 23 июня 2019

Я бы старался избегать использования tokio::spawn() здесь, и попытался бы обернуть все это в одно будущее (в общем, я думаю, что вы делаете tokio :: spawn только тогда, когда вам нет дела до результата или выполнения, что мы делаем здесь). Это должно облегчить ожидание завершения. Я не проверял это, но что-то в этом роде может помочь:

    let task = tokio::fs::read_dir(path)
        .flatten_stream()
        .for_each(move |entry| {
            let path = entry.path();
            if path.is_dir() {
                let task = visit_async(entry.path());
                future::Either::A(task)
            } else {
                println!("File: {:?}", path);
                future::Either::B(future::ok(()))
            }
        })
        .map_err(Error::from)
        .and_then(|_| {
            // Do some work once all tasks complete
        });
    Box::new(task)

Это приведет к тому, что асинхронные задачи будут выполняться последовательно. Вы можете использовать and_then вместо for_each для их параллельного выполнения, а затем into_future().and_then(|_| { ... }), чтобы настроить какое-либо действие для последующего выполнения.

...