Home > Enterprise >  kotlin async/await: send multiple GET requests and await for the results later downstream
kotlin async/await: send multiple GET requests and await for the results later downstream

Time:09-30

I am trying to send multiple GET requests to the server but await for the results as it comes in like the code below:

suspend fun getRequests(value: Int): Int {
    val rnd = (100..3000L).random()
    println("Delay for $rnd")  // simulate network time delay.
    delay(rnd)

    return value * 2
}

fun main() {
    runBlocking {
        launch(Dispatchers.IO) {
            val myList: ArrayList<Deferred<Int>> = ArrayList()
            for (i in 1..10) {
                println("GET request sent: no$i")
                myList.add(async {
                    getRequests(i)
                })
            }
            myList.forEach {
                println(it.await())  // process the results
            }
        }
    }
}

As you can see, there is a delay in the getRequests to simulate network latency. My objective is to await for the result as it comes in and process it. However, from what I can tell in the results printed out to console (as below), it is being processed sequentially...

GET request sent: no1
GET request sent: no2
GET request sent: no3
GET request sent: no4
GET request sent: no5
GET request sent: no6
GET request sent: no7
Delay for 706
Delay for 487
Delay for 2894
Delay for 1857
Delay for 353
GET request sent: no8
Delay for 2216
GET request sent: no9
Delay for 546
GET request sent: no10
Delay for 1806
Delay for 1291
Delay for 2830
2
4
6
8
10
12
14
16
18
20

How should I do it such that the results can be processed the moment it comes in, and not wait for results sequentially?

CodePudding user response:

In your code:

myList.forEach {
    println(it.await())  // process the results
}

You are waiting for the responses sequentially, i.e. you first await for the first Deffered to complete then the next one and so on. Instead if you want to process the responses as they arrive, you should do that in the async block itself. And when you do that, you will realize that its better to use launch instead of async because there is no value to return.

fun main() {
    runBlocking {
        for (i in 1..10) {
            launch(Dispatchers.IO) {
                val result = getRequests(i)
                println(result)
            }
        }
    }
}

Try it yourself

Sample output:

Delay for 1 -> 1447
Delay for 2 -> 2370
Delay for 3 -> 861
Delay for 5 -> 1156
Delay for 4 -> 973
Delay for 6 -> 827
Delay for 7 -> 1975
Delay for 8 -> 2560
Delay for 9 -> 2850
Delay for 10 -> 1989
12
6
8
10
2
14
20
4
16
18

Here you can see that first value printed is 12 because the delay for 6 was minimum (827), next 6 is printed because 3 has the next higher delay (861) and so on...

CodePudding user response:

I assume you need the result as list, since there is myList in your sample code, the other solution is

fun main() {
    runBlocking {
        (1..10).toList()
            .map { 
                async {
                    getRequests(it)
                } 
            }
            .awaitAll()
            .forEach {
                println(it)
            }
    }
}
  • Related