blob: 30bcef8bdbae12c0165ebcbb3ea877c50a428cda [file] [log] [blame] [view]
Roman Elizarov660c2d72020-02-14 13:18:37 +03001<!--- TEST_NAME ComposingGuideTest -->
hadihariri7db55532018-09-15 10:35:08 +02002
Prendotab8a559d2018-11-30 16:24:23 +03003**Table of contents**
hadihariri7db55532018-09-15 10:35:08 +02004
5<!--- TOC -->
6
Roman Elizarov3258e1f2019-08-22 20:08:48 +03007* [Composing Suspending Functions](#composing-suspending-functions)
hadihariri7db55532018-09-15 10:35:08 +02008 * [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
Roman Elizarov660c2d72020-02-14 13:18:37 +030014<!--- END -->
hadihariri7db55532018-09-15 10:35:08 +020015
Roman Elizarov3258e1f2019-08-22 20:08:48 +030016## Composing Suspending Functions
hadihariri7db55532018-09-15 10:35:08 +020017
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
Alexander Prendotacbeef102018-09-27 18:42:04 +030026<div class="sample" markdown="1" theme="idea" data-highlight-only>
27
hadihariri7db55532018-09-15 10:35:08 +020028```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
Alexander Prendotacbeef102018-09-27 18:42:04 +030040</div>
41
hadihariri7db55532018-09-15 10:35:08 +020042
Yanis Batura8ab21302019-08-08 16:51:03 +070043What do we do if we need them to be invoked _sequentially_ &mdash; 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
hadihariri7db55532018-09-15 10:35:08 +020046to 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
Prendota65e6c8c2018-10-17 11:51:08 +030052<!--- CLEAR -->
53
54<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +030055
hadihariri7db55532018-09-15 10:35:08 +020056```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +030057import kotlinx.coroutines.*
58import kotlin.system.*
59
60fun main() = runBlocking<Unit> {
61//sampleStart
hadihariri7db55532018-09-15 10:35:08 +020062 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")
Prendota65e6c8c2018-10-17 11:51:08 +030068//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
hadihariri7db55532018-09-15 10:35:08 +020079}
80```
81
Alexander Prendotacbeef102018-09-27 18:42:04 +030082</div>
83
Adam Howardf13549a2020-06-02 11:17:46 +010084> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-compose-01.kt).
hadihariri7db55532018-09-15 10:35:08 +020085
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
Yanis Batura8ab21302019-08-08 16:51:03 +070097What if there are no dependencies between invocations of `doSomethingUsefulOne` and `doSomethingUsefulTwo` and
hadihariri7db55532018-09-15 10:35:08 +020098we 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
Yanis Batura8ab21302019-08-08 16:51:03 +0700102does not carry any resulting value, while `async` returns a [Deferred] &mdash; a light-weight non-blocking future
hadihariri7db55532018-09-15 10:35:08 +0200103that 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
Alexander Prendotacbeef102018-09-27 18:42:04 +0300106
Prendota65e6c8c2018-10-17 11:51:08 +0300107<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300108
hadihariri7db55532018-09-15 10:35:08 +0200109```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300110import kotlinx.coroutines.*
111import kotlin.system.*
112
113fun main() = runBlocking<Unit> {
114//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200115 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")
Prendota65e6c8c2018-10-17 11:51:08 +0300121//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
hadihariri7db55532018-09-15 10:35:08 +0200132}
133```
134
Alexander Prendotacbeef102018-09-27 18:42:04 +0300135</div>
136
Adam Howardf13549a2020-06-02 11:17:46 +0100137> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-compose-02.kt).
hadihariri7db55532018-09-15 10:35:08 +0200138
139It produces something like this:
140
141```text
142The answer is 42
143Completed in 1017 ms
144```
145
146<!--- TEST ARBITRARY_TIME -->
147
Yanis Batura8ab21302019-08-08 16:51:03 +0700148This is twice as fast, because the two coroutines execute concurrently.
Inegoebe519a2019-04-21 13:22:27 +0700149Note that concurrency with coroutines is always explicit.
hadihariri7db55532018-09-15 10:35:08 +0200150
151### Lazily started async
152
Yanis Batura8ab21302019-08-08 16:51:03 +0700153Optionally, [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
hadihariri7db55532018-09-15 10:35:08 +0200156is invoked. Run the following example:
157
Prendota65e6c8c2018-10-17 11:51:08 +0300158<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300159
hadihariri7db55532018-09-15 10:35:08 +0200160```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300161import kotlinx.coroutines.*
162import kotlin.system.*
163
164fun main() = runBlocking<Unit> {
165//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200166 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")
Prendota65e6c8c2018-10-17 11:51:08 +0300175//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
hadihariri7db55532018-09-15 10:35:08 +0200186}
187```
188
Alexander Prendotacbeef102018-09-27 18:42:04 +0300189</div>
190
Adam Howardf13549a2020-06-02 11:17:46 +0100191> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-compose-03.kt).
hadihariri7db55532018-09-15 10:35:08 +0200192
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
Yanis Batura8ab21302019-08-08 16:51:03 +0700206Note 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.
hadihariri7db55532018-09-15 10:35:08 +0200209The 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`
Yanis Batura8ab21302019-08-08 16:51:03 +0700215_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
hadihariri7db55532018-09-15 10:35:08 +0200218to use the resulting deferred value to get the result.
219
Alexander Prendotacbeef102018-09-27 18:42:04 +0300220<div class="sample" markdown="1" theme="idea" data-highlight-only>
221
hadihariri7db55532018-09-15 10:35:08 +0200222```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
Alexander Prendotacbeef102018-09-27 18:42:04 +0300234</div>
235
Inegoebe519a2019-04-21 13:22:27 +0700236Note that these `xxxAsync` functions are **not** _suspending_ functions. They can be used from anywhere.
hadihariri7db55532018-09-15 10:35:08 +0200237However, 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:
Alexander Prendotacbeef102018-09-27 18:42:04 +0300241
Prendota65e6c8c2018-10-17 11:51:08 +0300242<!--- CLEAR -->
243
244<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
hadihariri7db55532018-09-15 10:35:08 +0200245
246```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300247import kotlinx.coroutines.*
248import kotlin.system.*
249
250//sampleStart
Inegoebe519a2019-04-21 13:22:27 +0700251// note that we don't have `runBlocking` to the right of `main` in this example
Prendota65e6c8c2018-10-17 11:51:08 +0300252fun main() {
hadihariri7db55532018-09-15 10:35:08 +0200253 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}
Prendota65e6c8c2018-10-17 11:51:08 +0300265//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}
hadihariri7db55532018-09-15 10:35:08 +0200284```
285
Alexander Prendotacbeef102018-09-27 18:42:04 +0300286</div>
287
Adam Howardf13549a2020-06-02 11:17:46 +0100288> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt).
hadihariri7db55532018-09-15 10:35:08 +0200289
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
Yanis Batura8ab21302019-08-08 16:51:03 +0700297reasons explained below.
hadihariri7db55532018-09-15 10:35:08 +0200298
Yanis Batura8ab21302019-08-08 16:51:03 +0700299Consider what happens if between the `val one = somethingUsefulOneAsync()` line and `one.await()` expression there is some logic
hadihariri7db55532018-09-15 10:35:08 +0200300error 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
Yanis Batura8ab21302019-08-08 16:51:03 +0700302could 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
hadihariri7db55532018-09-15 10:35:08 +0200304concurrency, as shown in the section below.
305
306### Structured concurrency with async
307
Yanis Batura8ab21302019-08-08 16:51:03 +0700308Let us take the [Concurrent using async](#concurrent-using-async) example and extract a function that
hadihariri7db55532018-09-15 10:35:08 +0200309concurrently performs `doSomethingUsefulOne` and `doSomethingUsefulTwo` and returns the sum of their results.
Yanis Batura8ab21302019-08-08 16:51:03 +0700310Because the [async] coroutine builder is defined as an extension on [CoroutineScope], we need to have it in the
Roman Elizaroveb1b5db2020-10-16 10:45:08 +0300311scope and that is what the [coroutineScope][_coroutineScope] function provides:
hadihariri7db55532018-09-15 10:35:08 +0200312
Alexander Prendotacbeef102018-09-27 18:42:04 +0300313<div class="sample" markdown="1" theme="idea" data-highlight-only>
314
hadihariri7db55532018-09-15 10:35:08 +0200315```kotlin
316suspend fun concurrentSum(): Int = coroutineScope {
317 val one = async { doSomethingUsefulOne() }
318 val two = async { doSomethingUsefulTwo() }
Benoît Quenaudonf62acb62019-01-29 11:35:13 -0500319 one.await() + two.await()
hadihariri7db55532018-09-15 10:35:08 +0200320}
321```
322
Alexander Prendotacbeef102018-09-27 18:42:04 +0300323</div>
324
Yanis Batura8ab21302019-08-08 16:51:03 +0700325This 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.
Alexander Prendotacbeef102018-09-27 18:42:04 +0300327
Prendota65e6c8c2018-10-17 11:51:08 +0300328<!--- CLEAR -->
329
330<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
hadihariri7db55532018-09-15 10:35:08 +0200331
332```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300333import kotlinx.coroutines.*
334import kotlin.system.*
335
336fun main() = runBlocking<Unit> {
Roman Elizarovdbb75d82019-02-20 13:28:40 +0300337//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200338 val time = measureTimeMillis {
339 println("The answer is ${concurrentSum()}")
340 }
341 println("Completed in $time ms")
Roman Elizarovdbb75d82019-02-20 13:28:40 +0300342//sampleEnd
Prendota65e6c8c2018-10-17 11:51:08 +0300343}
344
345suspend fun concurrentSum(): Int = coroutineScope {
346 val one = async { doSomethingUsefulOne() }
347 val two = async { doSomethingUsefulTwo() }
Benoît Quenaudonf62acb62019-01-29 11:35:13 -0500348 one.await() + two.await()
Prendota65e6c8c2018-10-17 11:51:08 +0300349}
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
hadihariri7db55532018-09-15 10:35:08 +0200359}
360```
361
Alexander Prendotacbeef102018-09-27 18:42:04 +0300362</div>
363
Adam Howardf13549a2020-06-02 11:17:46 +0100364> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-compose-05.kt).
hadihariri7db55532018-09-15 10:35:08 +0200365
Yanis Batura8ab21302019-08-08 16:51:03 +0700366We still have concurrent execution of both operations, as evident from the output of the above `main` function:
hadihariri7db55532018-09-15 10:35:08 +0200367
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
Prendota65e6c8c2018-10-17 11:51:08 +0300377<!--- CLEAR -->
378
379<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300380
hadihariri7db55532018-09-15 10:35:08 +0200381```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300382import kotlinx.coroutines.*
383
384fun main() = runBlocking<Unit> {
hadihariri7db55532018-09-15 10:35:08 +0200385 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 }
Benoît Quenaudonf62acb62019-01-29 11:35:13 -0500405 one.await() + two.await()
hadihariri7db55532018-09-15 10:35:08 +0200406}
407```
408
Alexander Prendotacbeef102018-09-27 18:42:04 +0300409</div>
410
Adam Howardf13549a2020-06-02 11:17:46 +0100411> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-compose-06.kt).
hadihariri7db55532018-09-15 10:35:08 +0200412
Yanis Batura8ab21302019-08-08 16:51:03 +0700413Note how both the first `async` and the awaiting parent are cancelled on failure of one of the children
414(namely, `two`):
hadihariri7db55532018-09-15 10:35:08 +0200415```text
416Second child throws an exception
417First child was cancelled
418Computation failed with ArithmeticException
419```
420
421<!--- TEST -->
422
Roman Elizarov99c28aa2018-09-23 18:42:36 +0300423<!--- MODULE kotlinx-coroutines-core -->
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300424<!--- INDEX kotlinx.coroutines -->
Vsevolod Tolstopyatov167c44e2020-11-30 05:36:23 -0800425
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300426[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html
427[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html
428[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html
429[Deferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html
430[CoroutineStart.LAZY]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/-l-a-z-y.html
431[Deferred.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html
432[Job.start]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/start.html
433[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html
434[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
Roman Elizaroveb1b5db2020-10-16 10:45:08 +0300435[_coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html
Vsevolod Tolstopyatov167c44e2020-11-30 05:36:23 -0800436
Roman Elizarov99c28aa2018-09-23 18:42:36 +0300437<!--- END -->