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 192We can define async-style functions that invoke `doSomethingUsefulOne` and `doSomethingUsefulTwo` 193_asynchronously_ using the [async] coroutine builder using a [GlobalScope] reference to 194opt-out of the structured concurrency. 195We name such functions with the 196"...Async" suffix to highlight the fact that they only start asynchronous computation and one needs 197to use the resulting deferred value to get the result. 198 199> [GlobalScope] is a delicate API that can backfire in non-trivial ways, one of which will be explained 200> below, so you must explicitly opt-in into using `GlobalScope` with `@OptIn(DelicateCoroutinesApi::class)`. 201> 202{type="note"} 203 204```kotlin 205// The result type of somethingUsefulOneAsync is Deferred<Int> 206@OptIn(DelicateCoroutinesApi::class) 207fun somethingUsefulOneAsync() = GlobalScope.async { 208 doSomethingUsefulOne() 209} 210 211// The result type of somethingUsefulTwoAsync is Deferred<Int> 212@OptIn(DelicateCoroutinesApi::class) 213fun somethingUsefulTwoAsync() = GlobalScope.async { 214 doSomethingUsefulTwo() 215} 216``` 217 218Note that these `xxxAsync` functions are **not** _suspending_ functions. They can be used from anywhere. 219However, their use always implies asynchronous (here meaning _concurrent_) execution of their action 220with the invoking code. 221 222The following example shows their use outside of coroutine: 223 224<!--- CLEAR --> 225 226```kotlin 227import kotlinx.coroutines.* 228import kotlin.system.* 229 230//sampleStart 231// note that we don't have `runBlocking` to the right of `main` in this example 232fun main() { 233 val time = measureTimeMillis { 234 // we can initiate async actions outside of a coroutine 235 val one = somethingUsefulOneAsync() 236 val two = somethingUsefulTwoAsync() 237 // but waiting for a result must involve either suspending or blocking. 238 // here we use `runBlocking { ... }` to block the main thread while waiting for the result 239 runBlocking { 240 println("The answer is ${one.await() + two.await()}") 241 } 242 } 243 println("Completed in $time ms") 244} 245//sampleEnd 246 247@OptIn(DelicateCoroutinesApi::class) 248fun somethingUsefulOneAsync() = GlobalScope.async { 249 doSomethingUsefulOne() 250} 251 252@OptIn(DelicateCoroutinesApi::class) 253fun somethingUsefulTwoAsync() = GlobalScope.async { 254 doSomethingUsefulTwo() 255} 256 257suspend fun doSomethingUsefulOne(): Int { 258 delay(1000L) // pretend we are doing something useful here 259 return 13 260} 261 262suspend fun doSomethingUsefulTwo(): Int { 263 delay(1000L) // pretend we are doing something useful here, too 264 return 29 265} 266``` 267{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} 268 269> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt). 270> 271{type="note"} 272 273<!--- TEST ARBITRARY_TIME 274The answer is 42 275Completed in 1085 ms 276--> 277 278> This programming style with async functions is provided here only for illustration, because it is a popular style 279> in other programming languages. Using this style with Kotlin coroutines is **strongly discouraged** for the 280> reasons explained below. 281> 282{type="note"} 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