libutil: asyncSpread
this could be a method of AsyncCollect, but putting there would require another overload that synthesizes the empty key types we use here. even then a separate function is slightly nicer because it spares us writing things like `asyncCollect(someIterableOfPromises).collect()` or similar Change-Id: Ia92abc105016414a2171e237f2a0bc6233d3ccbf
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
/// @file
|
||||
|
||||
#include "lix/libutil/result.hh"
|
||||
#include <concepts>
|
||||
#include <kj/async.h>
|
||||
#include <kj/common.h>
|
||||
#include <kj/vector.h>
|
||||
@@ -101,4 +103,37 @@ AsyncCollect<K, V> asyncCollect(kj::Array<std::pair<K, kj::Promise<V>>> promises
|
||||
return AsyncCollect<K, V>(std::move(promises));
|
||||
}
|
||||
|
||||
/**
|
||||
* Run `fn` for every item in the `input` range asynchronously, using
|
||||
* the same fail-fast semantics as `asyncCollect`. `asyncSpread` is a
|
||||
* shorthand for calling `asyncCollect` with `std::tuple()` values as
|
||||
* keys, awaiting that to completion, and propagating all exceptions.
|
||||
*/
|
||||
template<typename Input, typename Fn>
|
||||
kj::Promise<Result<void>> asyncSpread(Input && input, Fn fn)
|
||||
requires requires {
|
||||
{
|
||||
fn(*begin(input))
|
||||
} -> std::same_as<kj::Promise<Result<void>>>;
|
||||
}
|
||||
{
|
||||
kj::Vector<std::pair<std::tuple<>, kj::Promise<Result<void>>>> children;
|
||||
if constexpr (requires { input.size(); }) {
|
||||
children.reserve(input.size());
|
||||
}
|
||||
|
||||
for (auto & i : input) {
|
||||
children.add(std::tuple(), fn(i));
|
||||
}
|
||||
|
||||
auto collect = asyncCollect(children.releaseAsArray());
|
||||
while (auto r = co_await collect.next()) {
|
||||
if (!r->second.has_value()) {
|
||||
co_return std::move(r->second);
|
||||
}
|
||||
}
|
||||
|
||||
co_return result::success();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user