A pattern that is often used goes something like this:
.each<void, QByteArray>([](const QByteArray &arg, KAsync::Future<void> &future) { ..... if (...) { auto subjob = foo() subjob.template then<void>([&future](){future.setFinished();}).exec(); } else if (...) { future.setFinished(); } else { future.setError(....); } }).exec();
So we have a bunch of decision logic, that depends on the evaluation of some arguments, that will then execute one or the other subjob, or result in an error etc.
This code ends up being both overly verbose, and especially error prone. We have to guarantee that future.setFinished is always called, always only once, which quickly becomes non trivial if the logic ends up being a bit more complex.
The verbosity of
subjob.template then<void>([&future](){future.setFinished();}).exec();
couly easily be fixed by allowing
subjob.template then<void>(future).exec();
However, the IMO better solution would be to allow:
.each<void, QByteArray>([](const QByteArray &arg) -> Async::Job<void> { ..... if (...) { return foo(); } else if (...) { return KAsync::null(); } return KAsync::error(....); }).exec();
This solution is less error prone, because the compiler can ensure that all codepaths contain a valid return value in form of a job, and thus also always only one return value. It still allows the logic to depend on the result of the previous job (arg), and is considerably less verbose than the first solution.
Also, this way error propagation doesn't have to be done manually and is covered by kasync.