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