1<!--- TEST_NAME ComposingGuideTest --> 2 3[//]: # (title: Composing suspending functions) 4 5This section covers various approaches to composition of suspending functions. 6 7## Sequential by default 8 9Assume that we have two suspending functions defined elsewhere that do something useful like some kind of 10remote service call or computation. We just pretend they are useful, but actually each one just 11delays for a second for the purpose of this example: 12 13```kotlin 14suspend fun doSomethingUsefulOne(): Int { 15 delay(1000L) // pretend we are doing something useful here 16 return 13 17} 18 19suspend fun doSomethingUsefulTwo(): Int { 20 delay(1000L) // pretend we are doing something useful here, too 21 return 29 22} 23``` 24 25What do we do if we need them to be invoked _sequentially_ — first `doSomethingUsefulOne` _and then_ 26`doSomethingUsefulTwo`, and compute the sum of their results? 27In practice, we do this if we use the result of the first function to make a decision on whether we need 28to invoke the second one or to decide on how to invoke it. 29 30We use a normal sequential invocation, because the code in the coroutine, just like in the regular 31code, is _sequential_ by default. The following example demonstrates it by measuring the total 32time it takes to execute both suspending functions: 33 34<!--- CLEAR --> 35 36```kotlin 37import kotlinx.coroutines.* 38import kotlin.system.* 39 40fun main() = runBlocking<Unit> { 41//sampleStart 42 val time = measureTimeMillis { 43 val one = doSomethingUsefulOne() 44 val two = doSomethingUsefulTwo() 45 println("The answer is ${one + two}") 46 } 47 println("Completed in $time ms") 48//sampleEnd 49} 50 51suspend fun doSomethingUsefulOne(): Int { 52 delay(1000L) // pretend we are doing something useful here 53 return 13 54} 55 56suspend fun doSomethingUsefulTwo(): Int { 57 delay(1000L) // pretend we are doing something useful here, too 58 return 29 59} 60``` 61{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} 62 63> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-compose-01.kt). 64> 65{type="note"} 66 67It produces something like this: 68 69```text 70The answer is 42 71Completed in 2017 ms 72``` 73 74<!--- TEST ARBITRARY_TIME --> 75 76## Concurrent using async 77 78What if there are no dependencies between invocations of `doSomethingUsefulOne` and `doSomethingUsefulTwo` and 79we want to get the answer faster, by doing both _concurrently_? This is where [async] comes to help. 80 81Conceptually, [async] is just like [launch]. It starts a separate coroutine which is a light-weight thread 82that works concurrently with all the other coroutines. The difference is that `launch` returns a [Job] and 83does not carry any resulting value, while `async` returns a [Deferred] — a light-weight non-blocking future 84that represents a promise to provide a result later. You can use `.await()` on a deferred value to get its eventual result, 85but `Deferred` is also a `Job`, so you can cancel it if needed. 86 87```kotlin 88import kotlinx.coroutines.* 89import kotlin.system.* 90 91fun main() = runBlocking<Unit> { 92//sampleStart 93 val time = measureTimeMillis { 94 val one = async { doSomethingUsefulOne() } 95 val two = async { doSomethingUsefulTwo() } 96 println("The answer is ${one.await() + two.await()}") 97 } 98 println("Completed in $time ms") 99//sampleEnd 100} 101 102suspend fun doSomethingUsefulOne(): Int { 103 delay(1000L) // pretend we are doing something useful here 104 return 13 105} 106 107suspend fun doSomethingUsefulTwo(): Int { 108 delay(1000L) // pretend we are doing something useful here, too 109 return 29 110} 111``` 112{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} 113 114> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-compose-02.kt). 115> 116{type="note"} 117 118It produces something like this: 119 120```text 121The answer is 42 122Completed in 1017 ms 123``` 124 125<!--- TEST ARBITRARY_TIME --> 126 127This is twice as fast, because the two coroutines execute concurrently. 128Note that concurrency with coroutines is always explicit. 129 130## Lazily started async 131 132Optionally, [async] can be made lazy by setting its `start` parameter to [CoroutineStart.LAZY]. 133In this mode it only starts the coroutine when its result is required by 134[await][Deferred.await], or if its `Job`'s [start][Job.start] function 135is invoked. Run the following example: 136 137```kotlin 138import kotlinx.coroutines.* 139import kotlin.system.* 140 141fun main() = runBlocking<Unit> { 142//sampleStart 143 val time = measureTimeMillis { 144 val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() } 145 val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() } 146 // some computation 147 one.start() // start the first one 148 two.start() // start the second one 149 println("The answer is ${one.await() + two.await()}") 150 } 151 println("Completed in $time ms") 152//sampleEnd 153} 154 155suspend fun doSomethingUsefulOne(): Int { 156 delay(1000L) // pretend we are doing something useful here 157 return 13 158} 159 160suspend fun doSomethingUsefulTwo(): Int { 161 delay(1000L) // pretend we are doing something useful here, too 162 return 29 163} 164``` 165{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} 166 167> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-compose-03.kt). 168> 169{type="note"} 170 171It produces something like this: 172 173```text 174The answer is 42 175Completed in 1017 ms 176``` 177 178<!--- TEST ARBITRARY_TIME --> 179 180So, here the two coroutines are defined but not executed as in the previous example, but the control is given to 181the programmer on when exactly to start the execution by calling [start][Job.start]. We first 182start `one`, then start `two`, and then await for the individual coroutines to finish. 183 184Note that if we just call [await][Deferred.await] in `println` without first calling [start][Job.start] on individual 185coroutines, this will lead to sequential behavior, since [await][Deferred.await] starts the coroutine 186execution and waits for its finish, which is not the intended use-case for laziness. 187The use-case for `async(start = CoroutineStart.LAZY)` is a replacement for the 188standard `lazy` function in cases when computation of the value involves suspending functions. 189 190## Async-style functions 191 192> This programming style with async functions is provided here only for illustration, because it is a popular style 193> in other programming languages. Using this style with Kotlin coroutines is **strongly discouraged** for the 194> reasons explained below. 195> 196{type="note"} 197 198We can define async-style functions that invoke `doSomethingUsefulOne` and `doSomethingUsefulTwo` 199_asynchronously_ using the [async] coroutine builder using a [GlobalScope] reference to 200opt-out of the structured concurrency. 201We name such functions with the 202"...Async" suffix to highlight the fact that they only start asynchronous computation and one needs 203to use the resulting deferred value to get the result. 204 205> [GlobalScope] is a delicate API that can backfire in non-trivial ways, one of which will be explained 206> below, so you must explicitly opt-in into using `GlobalScope` with `@OptIn(DelicateCoroutinesApi::class)`. 207> 208{type="note"} 209 210```kotlin 211// The result type of somethingUsefulOneAsync is Deferred<Int> 212@OptIn(DelicateCoroutinesApi::class) 213fun somethingUsefulOneAsync() = GlobalScope.async { 214 doSomethingUsefulOne() 215} 216 217// The result type of somethingUsefulTwoAsync is Deferred<Int> 218@OptIn(DelicateCoroutinesApi::class) 219fun somethingUsefulTwoAsync() = GlobalScope.async { 220 doSomethingUsefulTwo() 221} 222``` 223 224Note that these `xxxAsync` functions are **not** _suspending_ functions. They can be used from anywhere. 225However, their use always implies asynchronous (here meaning _concurrent_) execution of their action 226with the invoking code. 227 228The following example shows their use outside of coroutine: 229 230<!--- CLEAR --> 231 232```kotlin 233import kotlinx.coroutines.* 234import kotlin.system.* 235 236//sampleStart 237// note that we don't have `runBlocking` to the right of `main` in this example 238fun main() { 239 val time = measureTimeMillis { 240 // we can initiate async actions outside of a coroutine 241 val one = somethingUsefulOneAsync() 242 val two = somethingUsefulTwoAsync() 243 // but waiting for a result must involve either suspending or blocking. 244 // here we use `runBlocking { ... }` to block the main thread while waiting for the result 245 runBlocking { 246 println("The answer is ${one.await() + two.await()}") 247 } 248 } 249 println("Completed in $time ms") 250} 251//sampleEnd 252 253@OptIn(DelicateCoroutinesApi::class) 254fun somethingUsefulOneAsync() = GlobalScope.async { 255 doSomethingUsefulOne() 256} 257 258@OptIn(DelicateCoroutinesApi::class) 259fun somethingUsefulTwoAsync() = GlobalScope.async { 260 doSomethingUsefulTwo() 261} 262 263suspend fun doSomethingUsefulOne(): Int { 264 delay(1000L) // pretend we are doing something useful here 265 return 13 266} 267 268suspend fun doSomethingUsefulTwo(): Int { 269 delay(1000L) // pretend we are doing something useful here, too 270 return 29 271} 272``` 273{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} 274 275> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt). 276> 277{type="note"} 278 279<!--- TEST ARBITRARY_TIME 280The answer is 42 281Completed in 1085 ms 282--> 283 284Consider what happens if between the `val one = somethingUsefulOneAsync()` line and `one.await()` expression there is some logic 285error in the code, and the program throws an exception, and the operation that was being performed by the program aborts. 286Normally, a global error-handler could catch this exception, log and report the error for developers, but the program 287could otherwise continue doing other operations. However, here we have `somethingUsefulOneAsync` still running in the background, 288even though the operation that initiated it was aborted. This problem does not happen with structured 289concurrency, as shown in the section below. 290 291## Structured concurrency with async 292 293Let us take the [Concurrent using async](#concurrent-using-async) example and extract a function that 294concurrently performs `doSomethingUsefulOne` and `doSomethingUsefulTwo` and returns the sum of their results. 295Because the [async] coroutine builder is defined as an extension on [CoroutineScope], we need to have it in the 296scope and that is what the [coroutineScope][_coroutineScope] function provides: 297 298```kotlin 299suspend fun concurrentSum(): Int = coroutineScope { 300 val one = async { doSomethingUsefulOne() } 301 val two = async { doSomethingUsefulTwo() } 302 one.await() + two.await() 303} 304``` 305 306This way, if something goes wrong inside the code of the `concurrentSum` function, and it throws an exception, 307all the coroutines that were launched in its scope will be cancelled. 308 309<!--- CLEAR --> 310 311```kotlin 312import kotlinx.coroutines.* 313import kotlin.system.* 314 315fun main() = runBlocking<Unit> { 316//sampleStart 317 val time = measureTimeMillis { 318 println("The answer is ${concurrentSum()}") 319 } 320 println("Completed in $time ms") 321//sampleEnd 322} 323 324suspend fun concurrentSum(): Int = coroutineScope { 325 val one = async { doSomethingUsefulOne() } 326 val two = async { doSomethingUsefulTwo() } 327 one.await() + two.await() 328} 329 330suspend fun doSomethingUsefulOne(): Int { 331 delay(1000L) // pretend we are doing something useful here 332 return 13 333} 334 335suspend fun doSomethingUsefulTwo(): Int { 336 delay(1000L) // pretend we are doing something useful here, too 337 return 29 338} 339``` 340{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} 341 342> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-compose-05.kt). 343> 344{type="note"} 345 346We still have concurrent execution of both operations, as evident from the output of the above `main` function: 347 348```text 349The answer is 42 350Completed in 1017 ms 351``` 352 353<!--- TEST ARBITRARY_TIME --> 354 355Cancellation is always propagated through coroutines hierarchy: 356 357<!--- CLEAR --> 358 359```kotlin 360import kotlinx.coroutines.* 361 362fun main() = runBlocking<Unit> { 363 try { 364 failedConcurrentSum() 365 } catch(e: ArithmeticException) { 366 println("Computation failed with ArithmeticException") 367 } 368} 369 370suspend fun failedConcurrentSum(): Int = coroutineScope { 371 val one = async<Int> { 372 try { 373 delay(Long.MAX_VALUE) // Emulates very long computation 374 42 375 } finally { 376 println("First child was cancelled") 377 } 378 } 379 val two = async<Int> { 380 println("Second child throws an exception") 381 throw ArithmeticException() 382 } 383 one.await() + two.await() 384} 385``` 386{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} 387 388> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-compose-06.kt). 389> 390{type="note"} 391 392Note how both the first `async` and the awaiting parent are cancelled on failure of one of the children 393(namely, `two`): 394```text 395Second child throws an exception 396First child was cancelled 397Computation failed with ArithmeticException 398``` 399 400<!--- TEST --> 401 402<!--- MODULE kotlinx-coroutines-core --> 403<!--- INDEX kotlinx.coroutines --> 404 405[async]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html 406[launch]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html 407[Job]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html 408[Deferred]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html 409[CoroutineStart.LAZY]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/-l-a-z-y/index.html 410[Deferred.await]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html 411[Job.start]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/start.html 412[GlobalScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html 413[CoroutineScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html 414[_coroutineScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html 415 416<!--- END --> 417