Мне кажется, что вы можете немного упростить свой код.Вам не требуется двухэтапная идиома, которая сначала запускает все задания async
, а затем запускает больше заданий, которые их ожидают.Вы можете просто launch
заданий и делегировать их в один и тот же блок.Таким образом, обратные вызовы будут естественным образом вызываться диспетчером вызывающей стороны, и только измененная контекстная область может быть вызвана только в измененном контексте с помощью invokeDispatcher
.
onFullCompletion
, который выглядит как фрагмент кода, принадлежащий вызывающей стороне.сторона, ниже execute
вызова.Поскольку execute
не выдает никаких исключений, вам не нужно try-finally
, чтобы получить его.
suspend fun <OUTPUT> execute(
jobs: List<suspend () -> OUTPUT>,
onTimeout: (jobIndex: Int) -> OUTPUT,
onFailure: (jobIndex: Int, exception: Throwable) -> OUTPUT,
onCompletion: suspend (jobIndex: Int, result: OUTPUT) -> Unit,
timeout: Long,
invokeDispatcher: CoroutineDispatcher = Dispatchers.Default
) {
coroutineScope {
jobs.mapIndexed { index, job ->
launch {
val output = try {
withTimeout(timeout) {
withContext(invokeDispatcher) {
job()
}
}
} catch (e: TimeoutCancellationException) {
onTimeout(index)
} catch (e: Exception) {
onFailure(index, e)
}
onCompletion(index, output)
}
}
}
}