Kotlin equivalent to C# Task.WhenAll - kotlin

Is there a Kotlin Equivalent of C#'s Task.WhenAll?
I came up with the code below, but I wonder if it is possible to write whenAll so that it only suspends once.
fun main(args: Array<String>) = runBlocking {
println("Start")
val serviceA = KotlinServiceA()
val serviceB = KotlinServiceB()
val deferredA = async(CommonPool) { serviceA.operationA() }
val deferredB = async(CommonPool) { serviceB.operationB() }
var tasks = arrayOf(deferredA, deferredB)
tasks.whenAll()
println("End")
}
suspend fun Array<Deferred<Unit>>.whenAll() : Unit {
for (task in this) {
task.await()
}
}

There is a awaitAll() function that does the job.
val deferredArray: Array<Deferred<Unit>> = arrayOf()
val awaitAllArray = awaitAll(*deferredArray)
If you work with Collection then you can use the awaitAll() extension function
val deferredList: List<Deferred<Unit>> = listOf()
val awaitAllList = deferredList.awaitAll()

Related

How to test a stateflow that updates (emits) infinitely?

See this answer for "Unit test the new Kotlin coroutine StateFlow"
and this issue in Kotlin coroutines GitHub repo..
How can I test all emissions of this StateFlow (results variable)?
class Emitter(dispatcher: CoroutineContext) {
private val coroutineScope = CoroutineScope(dispatcher)
private val source = MutableStateFlow("INITIAL")
private val _results = MutableStateFlow<String?>(null)
val results = _results.asStateFlow()
init {
source
.emitLatestEvery(5.seconds)
.conflate()
.map(String::lowercase)
.onEach(_results::emit)
.launchIn(coroutineScope)
}
#OptIn(ExperimentalCoroutinesApi::class)
private fun <T> Flow<T>.emitLatestEvery(duration: Duration) =
transformLatest {
while (true) {
emit(it)
delay(duration)
}
}
fun changeSource(s: String) {
source.value = s
}
}
Here is my test. It does not finish even if I use emitter.results.take(3).toList(results):
class EmitterTest {
#OptIn(ExperimentalCoroutinesApi::class)
#Test fun `Sample test`() = runTest {
val dispatcher = UnconfinedTestDispatcher(testScheduler)
val emitter = Emitter(dispatcher)
val results = mutableListOf<String?>()
val job = launch(dispatcher) { emitter.results.toList(results) }
emitter.changeSource("a")
emitter.changeSource("aB")
emitter.changeSource("AbC")
Assertions.assertThat(results).isEqualTo(listOf(null, "initial", "a", "ab", "abc"))
job.cancel()
}
}

How to access class methods from anonymous suspend function inside constructor in kotlin?

I want to be able to call functions from the anonymous constructor's suspend function in the following example:
data class SuspendableStep(
val condition: SuspendableCondition,
val continuation: Continuation<Unit>
)
class WaitCondition(cycles: Int) : SuspendableCondition() {
private val timer = SomeTimer(cycles)
override fun resume(): Boolean = timer.elapsed() // timer is handled somewhere else
override fun toString(): String = "WaitCondition_$timer"
}
class BasicContinuation : Continuation<Unit> {
var coroutine: Continuation<Unit>
override val context: CoroutineContext = EmptyCoroutineContext
private var nextStep: SuspendableStep? = null
constructor(task: suspend () -> Unit) {
coroutine = task.createCoroutine(completion = this)
}
override fun resumeWith(result: Result<Unit>) {
nextStep = null
result.exceptionOrNull()?.let { e -> Logger.handle("Error with plugin!", e) }
}
suspend fun wait(cycles: Int): Unit = suspendCoroutine {
check(cycles > 0) { "Wait cycles must be greater than 0." }
nextStep = SuspendableStep(WaitCondition(cycles), it)
}
}
fun main() {
BasicContinuation({
println("HELLO")
wait(1)
println("WORLD")
}).coroutine.resume(Unit)
}
There only other option I found was to override a suspend function by creating an anonymous inner class and calling another function to set the coroutine:
fun main() {
val bc = BasicContinuation() {
override suspend fun test() : Unit {
println("HELLO")
wait(1)
println("WORLD")
}
}
bc.set() // assign coroutine to suspend { test }.createCoroutine(completion = this)
bc.coroutine.resume(Unit)
}
I used CoroutineScope to extend the scope of the functions I could access:
class BasicContinuation : Continuation<Unit> {
var coroutine: Continuation<Unit>
override val context: CoroutineContext = EmptyCoroutineContext
private var nextStep: SuspendableStep? = null
constructor(task: (suspend BasicContinuation.(CoroutineScope) -> Unit)) {
coroutine = suspend { task.invoke(this, CoroutineScope(context)) }.createCoroutine(completion = this)
}
override fun resumeWith(result: Result<Unit>) {
nextStep = null
result.exceptionOrNull()?.let { e -> Logger.handle("Error with plugin!", e) }
}
suspend fun wait(cycles: Int): Unit = suspendCoroutine {
check(cycles > 0) { "Wait cycles must be greater than 0." }
nextStep = SuspendableStep(WaitCondition(cycles), it)
}
}
fun main() {
val bc = BasicContinuation({
println("Hello")
wait(1)
println("World")
})
bc.coroutine.resume(Unit) // print "Hello"
// increment timer
bc.coroutine.resume(Unit) // print "World
}

Is there a function that can write suspend function like Future&then in Dart?

sample code in dart:
void main() {
step1().then(step2).then(step3).then(print);
}
Future<String> step1() async {
return Future.value("setp1");
}
Future<int> step2(String input) async {
return Future.value(input.length);
}
Future<bool> step3(int input) async {
return Future.value(input > 3);
}
is there any way to write code in kotlin like this?
I use flow to write a simple code, but I won't find a way to simplify it
suspend fun step1(): String {
return "step1"
}
suspend fun step2(input: String): Int {
return input.length
}
suspend fun step3(input: Int): Boolean {
return input > 3
}
suspend fun execute() {
flowOf(step1())
.map { step2(it) }
.map { step3(it) }
.collect { print(it) }
}
I think you're saying you just want to run the suspend functions sequentially. If so, it doesn't look any different than non-suspending code.
suspend fun execute(){
val result1 = step1()
val result2 = step2(result1)
print(step3(result2))
}
If you really want to chain functions that are not extension functions, you can use run or let, although some might say this is less readable
suspend fun execute() = step1()
.run(::step2)
.run(::step3)
.run(::print)
If your functions are defined as extensions:
suspend fun String.step2() = length
suspend fun Int.step3() = this > 3
then you can chain directly:
suspend fun execute() = step1()
.step2()
.step3()
.run(::print)
kotlin coroutine does not adopt Promise like APIs (then/map/flatMap) because with suspend function, this can be done much easier
import kotlinx.coroutines.delay
import kotlinx.coroutines.yield
suspend fun main(args: Array<String>) {
val one = step1()
val two = step2(one)
val three = step3(two)
println(three)
}
suspend fun <T> resolve(value: T): T {
yield() // to simulate Future.resolve
return value
}
suspend fun step1() = resolve("100")
suspend fun step2(input: String) = resolve(input.length)
suspend fun step3(input: Int) = resolve(input > 3)
see discussions below
https://github.com/Kotlin/kotlinx.coroutines/issues/342
Using Coroutines in Kotlin
Synchronous run with three functions: here we perform three tasks in a coroutine and return the three results of the tasks in a Triple class.
suspend fun workWithThreeTimes(time1: Int, time2: Int, time3: Int): Triple<Result, Result, Result> {
return coroutineScope {
val result1 = manager.workForTime(time1) // work1
val result2 = manager.workForTime(time2) // work2 after work1
val result3 = manager.workForTime(time3) // work3 after work2
Triple(result1, result2, result3)
}
}
Parallel run with three functions: here we perform three works, in parallel, in a coroutine and return the three results of the tasks in a Triple class.
suspend fun workWithThreeTimesParallel(time1: Int, time2: Int, time3: Int): Triple<Result, Result, Result> {
return coroutineScope {
val work1 = async { manager.workForTime(time1) } // Async work1
val work2 = async { manager.workForTime(time2) } // Async work2 while work1 is working
val result3 = manager.workForTime(time3) // work3 while work1 and work2 are working
val result1 = work1.await() // non-blocking wait
val result2 = work2.await()// non-blocking wait
Triple(result1, result2, result3)
}
}

Is it possible to pass an argument into a sequence function?

I'm looking for a way to pass an argument into a Kotlin sequence function similar to how it works in JS:
function *gen () {
console.log(yield) // prints 1
console.log(yield) // prints 2
}
const it = gen()
it.next() // first iteration will execute the first yield and pause
it.next(1) // we pass 1 to the first yield which will be printed
it.next(2) // we pass 2 to the second yield which will be printed
Something like this in Kotlin:
fun main() {
val it = gen().iterator()
// Iterator#next() doesn't expect an argument
it.next(1)
it.next(2)
}
fun gen() = sequence {
println(yield(null)) // Would print 1
println(yield(null)) // Would print 2
}
Kotlin Sequences do not support passing arguments to each yield, but you have at least 2 ways to implement needed behaviour:
Using actors:
class NextQuery<A, T>(val arg: A, val next: CompletableDeferred<T> = CompletableDeferred())
fun test() = runBlocking {
val actor = GlobalScope.actor<NextQuery<String, Int>> {
for (nextQuery in channel) {
nextQuery.next.complete(nextQuery.arg.length)
}
}
val query1 = NextQuery<String, Int>("12345")
actor.send(query1)
println(query1.next.await())
val query2 = NextQuery<String, Int>("1234")
actor.send(query2)
println(query2.next.await())
}
Using channels:
class ArgSequenceScope<out A, in T>(
private val argChannel: ReceiveChannel<A>,
private val nextChannel: SendChannel<T>
) {
suspend fun yield(next: T) {
nextChannel.send(next)
}
suspend fun arg(): A = argChannel.receive()
}
class ArgSequence<in A, out T>(
private val argChannel: SendChannel<A>,
private val nextChannel: ReceiveChannel<T>
) {
suspend fun next(arg: A): T {
argChannel.send(arg)
return nextChannel.receive()
}
}
fun <A, T> sequenceWithArg(block: suspend ArgSequenceScope<A, T>.() -> Unit): ArgSequence<A, T> {
val argChannel = Channel<A>()
val nextChannel = Channel<T>()
val argSequenceScope = ArgSequenceScope(argChannel, nextChannel)
GlobalScope.launch {
argSequenceScope.block()
argChannel.close()
nextChannel.close()
}
return ArgSequence(argChannel, nextChannel)
}
fun test() {
val sequence = sequenceWithArg<String, Int> {
yield(arg().length)
yield(arg().length)
}
runBlocking {
println(sequence.next("12345"))
println(sequence.next("1234"))
}
}

A Kotlin service with request queue

I would like to design an a service with the following API:
suspend fun getUsers(request: Request): List<User>
Under the hood I would send a request to the server (doesn't matter how, but lets say it's a reactive WebClient), but here's a trick: I can only send requests as often as every 500 ms, otherwise I will get an error.
Could someone recommend me how I could implement it such way that when I call getUsers from a coroutine it suspends, the unit of work is being added to some queue of the service that has this method, then implemented at some point in time and returned the result?
I assume I can use some ReceiveChannel as a queue, have a for loop for its elements with a delay inside, but I'm a bit lost where to put this logic. Should this be like a background method that will run forever and gets called by getUsers? Probably the close method will never be called, so this method can also be suspended, but how do I pass the value back from this infinite running method to getUsers that needs the results?
EDIT
At the moment I'm thinking of a solution like this:
private const val REQUEST_INTERVAL = 500
#Service
class DelayedRequestSenderImpl<T> : DelayedRequestSender<T> {
private var lastRequestTime: LocalDateTime = LocalDateTime.now()
private val requestChannel: Channel<Deferred<T>> = Channel()
override suspend fun requestAsync(block: () -> T): Deferred<T> {
val deferred = GlobalScope.async(start = CoroutineStart.LAZY) { block() }
requestChannel.send(deferred)
return deferred
}
#PostConstruct
private fun startRequestProcessing() = GlobalScope.launch {
for (request in requestChannel) {
val now = LocalDateTime.now()
val diff = ChronoUnit.MILLIS.between(lastRequestTime, now)
if (diff < REQUEST_INTERVAL) {
delay(REQUEST_INTERVAL - diff)
lastRequestTime = now
}
request.start()
}
}
}
The problem I see here is that I have to generify the class to make the requestChannel generic, since the result of request may be anything. But this means that each instance of DelayedRequestSender will be tied to a particular type. Any advice on how to avoid this?
EDIT 2
Here's a refined version. The only possible flow that I see at the moment is that we have to make #PostConstruct method public in order to write any tests if we want or use reflection.
The idea was to not use GlobalScope and also have a separate Job for the processing method. Is this a fine approach?
interface DelayingSupplier {
suspend fun <T> supply(block: () -> T): T
}
#Service
class DelayingSupplierImpl(#Value("\${vk.request.interval}") private val interval: Int) : DelayingSupplier {
private var lastRequestTime: LocalDateTime = LocalDateTime.now()
private val requestChannel: Channel<Deferred<*>> = Channel()
private val coroutineScope = CoroutineScope(EmptyCoroutineContext)
override suspend fun <T> supply(block: () -> T): T {
val deferred = coroutineScope.async(start = CoroutineStart.LAZY) { block() }
requestChannel.send(deferred)
return deferred.await()
}
#PostConstruct
fun startProcessing() = coroutineScope.launch(context = Job(coroutineScope.coroutineContext[Job])) {
for (request in requestChannel) {
val now = LocalDateTime.now()
val diff = ChronoUnit.MILLIS.between(lastRequestTime, now)
if (diff < interval) {
delay(interval - diff)
}
lastRequestTime = LocalDateTime.now()
request.start()
}
}
}
I would recommend:
pushing your generics down to the function level
using an actor instead of your coroutine implementation (but possibly you prefer this).
Either way, this solution should let you use a single instance of your queue to handle the delay of all requests regardless of return type. (Apologies, I renamed some things to help my own conceptualization, hopefully this still makes sense):
private const val REQUEST_INTERVAL = 500
interface DelayedRequestHandler {
suspend fun <T> handleWithDelay(block: () -> T): T
}
class DelayedRequestHandlerImpl(requestInterval: Int = REQUEST_INTERVAL) : DelayedRequestHandler, CoroutineScope {
private val job = Job()
override val coroutineContext = Dispatchers.Unconfined + job
private val delayedHandlerActor = delayedRequestHandlerActor(requestInterval)
override suspend fun <T> handleWithDelay(block: () -> T): T {
val result = CompletableDeferred<T>()
delayedHandlerActor.send(DelayedHandlerMsg(result, block))
return result.await()
}
}
private data class DelayedHandlerMsg<RESULT>(val result: CompletableDeferred<RESULT>, val block: () -> RESULT)
private fun CoroutineScope.delayedRequestHandlerActor(requestInterval: Int) = actor<DelayedHandlerMsg<*>>() {
var lastRequestTime: LocalDateTime = LocalDateTime.now()
for (message in channel) {
try {
println("got a message processing")
val now = LocalDateTime.now()
val diff = ChronoUnit.MILLIS.between(lastRequestTime, now)
if (diff < requestInterval) {
delay(requestInterval - diff)
}
lastRequestTime = LocalDateTime.now()
#Suppress("UNCHECKED_CAST")
val msgCast = message as DelayedHandlerMsg<Any?>
val result = msgCast.block()
println(result)
msgCast.result.complete(result)
} catch (e: Exception) {
message.result.completeExceptionally(e)
}
}
}
fun main() = runBlocking {
val mydelayHandler = DelayedRequestHandlerImpl(2000)
val jobs = List(10) {
launch {
mydelayHandler.handleWithDelay {
"Result $it"
}
}
}
jobs.forEach { it.join() }
}
So this is the final implementation I came up with. Note the SupevisorJob as we don't want the processing to stop if one of requests fails, which is totally possible and fine (in my case at least).
Also, the option suggested by #Laurence might be better, but I decided to not use actors for now due to API being marked as obsolete.
#Service
class DelayingRequestSenderImpl(#Value("\${vk.request.interval}") private val interval: Int) : DelayingRequestSender {
private var lastRequestTime: LocalDateTime = LocalDateTime.now()
private val requestChannel: Channel<Deferred<*>> = Channel()
//SupervisorJob is used because we want to have continuous processing of requestChannel
//even if one of the requests fails
private val coroutineScope = CoroutineScope(SupervisorJob())
override suspend fun <T> request(block: () -> T): T {
val deferred = coroutineScope.async(start = CoroutineStart.LAZY) { block() }
requestChannel.send(deferred)
return deferred.await()
}
#PostConstruct
fun startProcessing() = coroutineScope.launch {
for (request in requestChannel) {
val now = LocalDateTime.now()
val diff = ChronoUnit.MILLIS.between(lastRequestTime, now)
if (diff < interval) {
delay(interval - diff)
}
lastRequestTime = LocalDateTime.now()
request.start()
}
}
}