• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_ &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
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] &mdash; 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