blob: ef1b032ddefc6e2bfb782bca9d9ff22d0fed6cc6 [file] [log] [blame] [view]
hadihariri7db55532018-09-15 10:35:08 +02001<!--- INCLUDE .*/example-([a-z]+)-([0-9a-z]+)\.kt
2/*
3 * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
4 */
5
6// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
7package kotlinx.coroutines.experimental.guide.$$1$$2
8
9import kotlinx.coroutines.experimental.*
10-->
11<!--- KNIT ../core/kotlinx-coroutines-core/test/guide/.*\.kt -->
12<!--- TEST_OUT ../core/kotlinx-coroutines-core/test/guide/test/ComposingGuideTest.kt
13// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
14package kotlinx.coroutines.experimental.guide.test
15
16import org.junit.Test
17
18class ComposingGuideTest {
19-->
20
21## Table of contents
22
23<!--- TOC -->
24
25* [Composing suspending functions](#composing-suspending-functions)
26 * [Sequential by default](#sequential-by-default)
27 * [Concurrent using async](#concurrent-using-async)
28 * [Lazily started async](#lazily-started-async)
29 * [Async-style functions](#async-style-functions)
30 * [Structured concurrency with async](#structured-concurrency-with-async)
31
32<!--- END_TOC -->
33
34## Composing suspending functions
35
36This section covers various approaches to composition of suspending functions.
37
38### Sequential by default
39
40Assume that we have two suspending functions defined elsewhere that do something useful like some kind of
41remote service call or computation. We just pretend they are useful, but actually each one just
42delays for a second for the purpose of this example:
43
44<!--- INCLUDE .*/example-compose-([0-9]+).kt
45import kotlin.system.*
46-->
47
Alexander Prendotacbeef102018-09-27 18:42:04 +030048<div class="sample" markdown="1" theme="idea" data-highlight-only>
49
hadihariri7db55532018-09-15 10:35:08 +020050```kotlin
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
Alexander Prendotacbeef102018-09-27 18:42:04 +030062</div>
63
hadihariri7db55532018-09-15 10:35:08 +020064<!--- INCLUDE .*/example-compose-([0-9]+).kt -->
65
66What do we do if need to invoke them _sequentially_ -- first `doSomethingUsefulOne` _and then_
67`doSomethingUsefulTwo` and compute the sum of their results?
68In practice we do this if we use the results of the first function to make a decision on whether we need
69to invoke the second one or to decide on how to invoke it.
70
71We use a normal sequential invocation, because the code in the coroutine, just like in the regular
72code, is _sequential_ by default. The following example demonstrates it by measuring the total
73time it takes to execute both suspending functions:
74
Alexander Prendotacbeef102018-09-27 18:42:04 +030075<div class="sample" markdown="1" theme="idea" data-highlight-only>
76
hadihariri7db55532018-09-15 10:35:08 +020077```kotlin
78fun main(args: Array<String>) = runBlocking<Unit> {
79 val time = measureTimeMillis {
80 val one = doSomethingUsefulOne()
81 val two = doSomethingUsefulTwo()
82 println("The answer is ${one + two}")
83 }
84 println("Completed in $time ms")
85}
86```
87
Alexander Prendotacbeef102018-09-27 18:42:04 +030088</div>
89
hadihariri7db55532018-09-15 10:35:08 +020090> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-compose-01.kt)
91
92It produces something like this:
93
94```text
95The answer is 42
96Completed in 2017 ms
97```
98
99<!--- TEST ARBITRARY_TIME -->
100
101### Concurrent using async
102
103What if there are no dependencies between invocation of `doSomethingUsefulOne` and `doSomethingUsefulTwo` and
104we want to get the answer faster, by doing both _concurrently_? This is where [async] comes to help.
105
106Conceptually, [async] is just like [launch]. It starts a separate coroutine which is a light-weight thread
107that works concurrently with all the other coroutines. The difference is that `launch` returns a [Job] and
108does not carry any resulting value, while `async` returns a [Deferred] -- a light-weight non-blocking future
109that represents a promise to provide a result later. You can use `.await()` on a deferred value to get its eventual result,
110but `Deferred` is also a `Job`, so you can cancel it if needed.
111
Alexander Prendotacbeef102018-09-27 18:42:04 +0300112
113<div class="sample" markdown="1" theme="idea" data-highlight-only>
114
hadihariri7db55532018-09-15 10:35:08 +0200115```kotlin
116fun main(args: Array<String>) = runBlocking<Unit> {
117 val time = measureTimeMillis {
118 val one = async { doSomethingUsefulOne() }
119 val two = async { doSomethingUsefulTwo() }
120 println("The answer is ${one.await() + two.await()}")
121 }
122 println("Completed in $time ms")
123}
124```
125
Alexander Prendotacbeef102018-09-27 18:42:04 +0300126</div>
127
hadihariri7db55532018-09-15 10:35:08 +0200128> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-compose-02.kt)
129
130It produces something like this:
131
132```text
133The answer is 42
134Completed in 1017 ms
135```
136
137<!--- TEST ARBITRARY_TIME -->
138
139This is twice as fast, because we have concurrent execution of two coroutines.
140Note, that concurrency with coroutines is always explicit.
141
142### Lazily started async
143
144There is a laziness option to [async] using an optional `start` parameter with a value of [CoroutineStart.LAZY].
145It starts coroutine only when its result is needed by some
146[await][Deferred.await] or if a [start][Job.start] function
147is invoked. Run the following example:
148
Alexander Prendotacbeef102018-09-27 18:42:04 +0300149<div class="sample" markdown="1" theme="idea" data-highlight-only>
150
hadihariri7db55532018-09-15 10:35:08 +0200151```kotlin
152fun main(args: Array<String>) = runBlocking<Unit> {
153 val time = measureTimeMillis {
154 val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }
155 val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() }
156 // some computation
157 one.start() // start the first one
158 two.start() // start the second one
159 println("The answer is ${one.await() + two.await()}")
160 }
161 println("Completed in $time ms")
162}
163```
164
Alexander Prendotacbeef102018-09-27 18:42:04 +0300165</div>
166
hadihariri7db55532018-09-15 10:35:08 +0200167> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-compose-03.kt)
168
169It produces something like this:
170
171```text
172The answer is 42
173Completed in 1017 ms
174```
175
176<!--- TEST ARBITRARY_TIME -->
177
178So, here the two coroutines are defined but not executed as in the previous example, but the control is given to
179the programmer on when exactly to start the execution by calling [start][Job.start]. We first
180start `one`, then start `two`, and then await for the individual coroutines to finish.
181
182Note, that if we have called [await][Deferred.await] in `println` and omitted [start][Job.start] on individual
183coroutines, then we would have got the sequential behaviour as [await][Deferred.await] starts the coroutine
184execution and waits for the execution to finish, which is not the intended use-case for laziness.
185The use-case for `async(start = CoroutineStart.LAZY)` is a replacement for the
186standard `lazy` function in cases when computation of the value involves suspending functions.
187
188### Async-style functions
189
190We can define async-style functions that invoke `doSomethingUsefulOne` and `doSomethingUsefulTwo`
191_asynchronously_ using [async] coroutine builder with an explicit [GlobalScope] reference.
192We name such functions with
193"Async" suffix to highlight the fact that they only start asynchronous computation and one needs
194to use the resulting deferred value to get the result.
195
Alexander Prendotacbeef102018-09-27 18:42:04 +0300196<div class="sample" markdown="1" theme="idea" data-highlight-only>
197
hadihariri7db55532018-09-15 10:35:08 +0200198```kotlin
199// The result type of somethingUsefulOneAsync is Deferred<Int>
200fun somethingUsefulOneAsync() = GlobalScope.async {
201 doSomethingUsefulOne()
202}
203
204// The result type of somethingUsefulTwoAsync is Deferred<Int>
205fun somethingUsefulTwoAsync() = GlobalScope.async {
206 doSomethingUsefulTwo()
207}
208```
209
Alexander Prendotacbeef102018-09-27 18:42:04 +0300210</div>
211
hadihariri7db55532018-09-15 10:35:08 +0200212Note, that these `xxxAsync` functions are **not** _suspending_ functions. They can be used from anywhere.
213However, their use always implies asynchronous (here meaning _concurrent_) execution of their action
214with the invoking code.
215
216The following example shows their use outside of coroutine:
Alexander Prendotacbeef102018-09-27 18:42:04 +0300217
218<div class="sample" markdown="1" theme="idea" data-highlight-only>
hadihariri7db55532018-09-15 10:35:08 +0200219
220```kotlin
221// note, that we don't have `runBlocking` to the right of `main` in this example
222fun main(args: Array<String>) {
223 val time = measureTimeMillis {
224 // we can initiate async actions outside of a coroutine
225 val one = somethingUsefulOneAsync()
226 val two = somethingUsefulTwoAsync()
227 // but waiting for a result must involve either suspending or blocking.
228 // here we use `runBlocking { ... }` to block the main thread while waiting for the result
229 runBlocking {
230 println("The answer is ${one.await() + two.await()}")
231 }
232 }
233 println("Completed in $time ms")
234}
235```
236
Alexander Prendotacbeef102018-09-27 18:42:04 +0300237</div>
238
hadihariri7db55532018-09-15 10:35:08 +0200239> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-compose-04.kt)
240
241<!--- TEST ARBITRARY_TIME
242The answer is 42
243Completed in 1085 ms
244-->
245
246> This programming style with async functions is provided here only for illustration, because it is a popular style
247in other programming languages. Using this style with Kotlin coroutines is **strongly discouraged** for the
248reasons that are explained below.
249
250Consider what happens if between `val one = somethingUsefulOneAsync()` line and `one.await()` expression there is some logic
251error in the code and the program throws an exception and the operation that was being performed by the program aborts.
252Normally, a global error-handler could catch this exception, log and report the error for developers, but the program
253could otherwise continue doing other operations. But here we have `somethingUsefulOneAsync` still running in background,
254despite the fact, that operation that had initiated it aborts. This problem does not happen with structured
255concurrency, as shown in the section below.
256
257### Structured concurrency with async
258
259Let us take [Concurrent using async](#concurrent-using-async) example and extract a function that
260concurrently performs `doSomethingUsefulOne` and `doSomethingUsefulTwo` and returns the sum of their results.
261Because [async] coroutines builder is defined as extension on [CoroutineScope] we need to have it in the
262scope and that is what [coroutineScope] function provides:
263
Alexander Prendotacbeef102018-09-27 18:42:04 +0300264<div class="sample" markdown="1" theme="idea" data-highlight-only>
265
hadihariri7db55532018-09-15 10:35:08 +0200266```kotlin
267suspend fun concurrentSum(): Int = coroutineScope {
268 val one = async { doSomethingUsefulOne() }
269 val two = async { doSomethingUsefulTwo() }
hadihariri7db55532018-09-15 10:35:08 +0200270 one.await() + two.await()
271}
272```
273
Alexander Prendotacbeef102018-09-27 18:42:04 +0300274</div>
275
hadihariri7db55532018-09-15 10:35:08 +0200276This way, if something goes wrong inside the code of `concurrentSum` function and it throws an exception,
277all the coroutines that were launched in its scope are cancelled.
Alexander Prendotacbeef102018-09-27 18:42:04 +0300278
279<div class="sample" markdown="1" theme="idea" data-highlight-only>
hadihariri7db55532018-09-15 10:35:08 +0200280
281```kotlin
282fun main(args: Array<String>) = runBlocking<Unit> {
283 val time = measureTimeMillis {
284 println("The answer is ${concurrentSum()}")
285 }
286 println("Completed in $time ms")
287}
288```
289
Alexander Prendotacbeef102018-09-27 18:42:04 +0300290</div>
291
hadihariri7db55532018-09-15 10:35:08 +0200292> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-compose-05.kt)
293
294We still have concurrent execution of both operations as evident from the output of the above main function:
295
296```text
297The answer is 42
298Completed in 1017 ms
299```
300
301<!--- TEST ARBITRARY_TIME -->
302
303Cancellation is always propagated through coroutines hierarchy:
304
Alexander Prendotacbeef102018-09-27 18:42:04 +0300305<div class="sample" markdown="1" theme="idea" data-highlight-only>
306
hadihariri7db55532018-09-15 10:35:08 +0200307```kotlin
308fun main(args: Array<String>) = runBlocking<Unit> {
309 try {
310 failedConcurrentSum()
311 } catch(e: ArithmeticException) {
312 println("Computation failed with ArithmeticException")
313 }
314}
315
316suspend fun failedConcurrentSum(): Int = coroutineScope {
317 val one = async<Int> {
318 try {
319 delay(Long.MAX_VALUE) // Emulates very long computation
320 42
321 } finally {
322 println("First child was cancelled")
323 }
324 }
325 val two = async<Int> {
326 println("Second child throws an exception")
327 throw ArithmeticException()
328 }
Vsevolod Tolstopyatovfac516f2018-09-28 14:38:29 +0300329 one.await() + two.await()
hadihariri7db55532018-09-15 10:35:08 +0200330}
331```
332
Alexander Prendotacbeef102018-09-27 18:42:04 +0300333</div>
334
hadihariri7db55532018-09-15 10:35:08 +0200335> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-compose-06.kt)
336
337Note, how both first `async` and awaiting parent are cancelled on the one child failure:
338```text
339Second child throws an exception
340First child was cancelled
341Computation failed with ArithmeticException
342```
343
344<!--- TEST -->
345
Roman Elizarov99c28aa2018-09-23 18:42:36 +0300346<!--- MODULE kotlinx-coroutines-core -->
347<!--- INDEX kotlinx.coroutines.experimental -->
348[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/async.html
349[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/launch.html
350[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-job/index.html
351[Deferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/index.html
352[CoroutineStart.LAZY]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-coroutine-start/-l-a-z-y.html
353[Deferred.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/await.html
354[Job.start]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-job/start.html
355[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-global-scope/index.html
356[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-coroutine-scope/index.html
357[coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/coroutine-scope.html
358<!--- END -->