blob: a703d8338a4b8d1b6ee5da28f41a41a8422a53bf [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
48```kotlin
49suspend fun doSomethingUsefulOne(): Int {
50 delay(1000L) // pretend we are doing something useful here
51 return 13
52}
53
54suspend fun doSomethingUsefulTwo(): Int {
55 delay(1000L) // pretend we are doing something useful here, too
56 return 29
57}
58```
59
60<!--- INCLUDE .*/example-compose-([0-9]+).kt -->
61
62What do we do if need to invoke them _sequentially_ -- first `doSomethingUsefulOne` _and then_
63`doSomethingUsefulTwo` and compute the sum of their results?
64In practice we do this if we use the results of the first function to make a decision on whether we need
65to invoke the second one or to decide on how to invoke it.
66
67We use a normal sequential invocation, because the code in the coroutine, just like in the regular
68code, is _sequential_ by default. The following example demonstrates it by measuring the total
69time it takes to execute both suspending functions:
70
71```kotlin
72fun main(args: Array<String>) = runBlocking<Unit> {
73 val time = measureTimeMillis {
74 val one = doSomethingUsefulOne()
75 val two = doSomethingUsefulTwo()
76 println("The answer is ${one + two}")
77 }
78 println("Completed in $time ms")
79}
80```
81
82> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-compose-01.kt)
83
84It produces something like this:
85
86```text
87The answer is 42
88Completed in 2017 ms
89```
90
91<!--- TEST ARBITRARY_TIME -->
92
93### Concurrent using async
94
95What if there are no dependencies between invocation of `doSomethingUsefulOne` and `doSomethingUsefulTwo` and
96we want to get the answer faster, by doing both _concurrently_? This is where [async] comes to help.
97
98Conceptually, [async] is just like [launch]. It starts a separate coroutine which is a light-weight thread
99that works concurrently with all the other coroutines. The difference is that `launch` returns a [Job] and
100does not carry any resulting value, while `async` returns a [Deferred] -- a light-weight non-blocking future
101that represents a promise to provide a result later. You can use `.await()` on a deferred value to get its eventual result,
102but `Deferred` is also a `Job`, so you can cancel it if needed.
103
104```kotlin
105fun main(args: Array<String>) = runBlocking<Unit> {
106 val time = measureTimeMillis {
107 val one = async { doSomethingUsefulOne() }
108 val two = async { doSomethingUsefulTwo() }
109 println("The answer is ${one.await() + two.await()}")
110 }
111 println("Completed in $time ms")
112}
113```
114
115> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-compose-02.kt)
116
117It produces something like this:
118
119```text
120The answer is 42
121Completed in 1017 ms
122```
123
124<!--- TEST ARBITRARY_TIME -->
125
126This is twice as fast, because we have concurrent execution of two coroutines.
127Note, that concurrency with coroutines is always explicit.
128
129### Lazily started async
130
131There is a laziness option to [async] using an optional `start` parameter with a value of [CoroutineStart.LAZY].
132It starts coroutine only when its result is needed by some
133[await][Deferred.await] or if a [start][Job.start] function
134is invoked. Run the following example:
135
136```kotlin
137fun main(args: Array<String>) = runBlocking<Unit> {
138 val time = measureTimeMillis {
139 val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }
140 val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() }
141 // some computation
142 one.start() // start the first one
143 two.start() // start the second one
144 println("The answer is ${one.await() + two.await()}")
145 }
146 println("Completed in $time ms")
147}
148```
149
150> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-compose-03.kt)
151
152It produces something like this:
153
154```text
155The answer is 42
156Completed in 1017 ms
157```
158
159<!--- TEST ARBITRARY_TIME -->
160
161So, here the two coroutines are defined but not executed as in the previous example, but the control is given to
162the programmer on when exactly to start the execution by calling [start][Job.start]. We first
163start `one`, then start `two`, and then await for the individual coroutines to finish.
164
165Note, that if we have called [await][Deferred.await] in `println` and omitted [start][Job.start] on individual
166coroutines, then we would have got the sequential behaviour as [await][Deferred.await] starts the coroutine
167execution and waits for the execution to finish, which is not the intended use-case for laziness.
168The use-case for `async(start = CoroutineStart.LAZY)` is a replacement for the
169standard `lazy` function in cases when computation of the value involves suspending functions.
170
171### Async-style functions
172
173We can define async-style functions that invoke `doSomethingUsefulOne` and `doSomethingUsefulTwo`
174_asynchronously_ using [async] coroutine builder with an explicit [GlobalScope] reference.
175We name such functions with
176"Async" suffix to highlight the fact that they only start asynchronous computation and one needs
177to use the resulting deferred value to get the result.
178
179```kotlin
180// The result type of somethingUsefulOneAsync is Deferred<Int>
181fun somethingUsefulOneAsync() = GlobalScope.async {
182 doSomethingUsefulOne()
183}
184
185// The result type of somethingUsefulTwoAsync is Deferred<Int>
186fun somethingUsefulTwoAsync() = GlobalScope.async {
187 doSomethingUsefulTwo()
188}
189```
190
191Note, that these `xxxAsync` functions are **not** _suspending_ functions. They can be used from anywhere.
192However, their use always implies asynchronous (here meaning _concurrent_) execution of their action
193with the invoking code.
194
195The following example shows their use outside of coroutine:
196
197```kotlin
198// note, that we don't have `runBlocking` to the right of `main` in this example
199fun main(args: Array<String>) {
200 val time = measureTimeMillis {
201 // we can initiate async actions outside of a coroutine
202 val one = somethingUsefulOneAsync()
203 val two = somethingUsefulTwoAsync()
204 // but waiting for a result must involve either suspending or blocking.
205 // here we use `runBlocking { ... }` to block the main thread while waiting for the result
206 runBlocking {
207 println("The answer is ${one.await() + two.await()}")
208 }
209 }
210 println("Completed in $time ms")
211}
212```
213
214> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-compose-04.kt)
215
216<!--- TEST ARBITRARY_TIME
217The answer is 42
218Completed in 1085 ms
219-->
220
221> This programming style with async functions is provided here only for illustration, because it is a popular style
222in other programming languages. Using this style with Kotlin coroutines is **strongly discouraged** for the
223reasons that are explained below.
224
225Consider what happens if between `val one = somethingUsefulOneAsync()` line and `one.await()` expression there is some logic
226error in the code and the program throws an exception and the operation that was being performed by the program aborts.
227Normally, a global error-handler could catch this exception, log and report the error for developers, but the program
228could otherwise continue doing other operations. But here we have `somethingUsefulOneAsync` still running in background,
229despite the fact, that operation that had initiated it aborts. This problem does not happen with structured
230concurrency, as shown in the section below.
231
232### Structured concurrency with async
233
234Let us take [Concurrent using async](#concurrent-using-async) example and extract a function that
235concurrently performs `doSomethingUsefulOne` and `doSomethingUsefulTwo` and returns the sum of their results.
236Because [async] coroutines builder is defined as extension on [CoroutineScope] we need to have it in the
237scope and that is what [coroutineScope] function provides:
238
239```kotlin
240suspend fun concurrentSum(): Int = coroutineScope {
241 val one = async { doSomethingUsefulOne() }
242 val two = async { doSomethingUsefulTwo() }
243 awaitAll(one, two)
244 one.await() + two.await()
245}
246```
247
248This way, if something goes wrong inside the code of `concurrentSum` function and it throws an exception,
249all the coroutines that were launched in its scope are cancelled.
250
251```kotlin
252fun main(args: Array<String>) = runBlocking<Unit> {
253 val time = measureTimeMillis {
254 println("The answer is ${concurrentSum()}")
255 }
256 println("Completed in $time ms")
257}
258```
259
260> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-compose-05.kt)
261
262We still have concurrent execution of both operations as evident from the output of the above main function:
263
264```text
265The answer is 42
266Completed in 1017 ms
267```
268
269<!--- TEST ARBITRARY_TIME -->
270
271Cancellation is always propagated through coroutines hierarchy:
272
273```kotlin
274fun main(args: Array<String>) = runBlocking<Unit> {
275 try {
276 failedConcurrentSum()
277 } catch(e: ArithmeticException) {
278 println("Computation failed with ArithmeticException")
279 }
280}
281
282suspend fun failedConcurrentSum(): Int = coroutineScope {
283 val one = async<Int> {
284 try {
285 delay(Long.MAX_VALUE) // Emulates very long computation
286 42
287 } finally {
288 println("First child was cancelled")
289 }
290 }
291 val two = async<Int> {
292 println("Second child throws an exception")
293 throw ArithmeticException()
294 }
295
296 awaitAll(one, two)
297 one.await() + two.await()
298}
299```
300
301> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-compose-06.kt)
302
303Note, how both first `async` and awaiting parent are cancelled on the one child failure:
304```text
305Second child throws an exception
306First child was cancelled
307Computation failed with ArithmeticException
308```
309
310<!--- TEST -->
311
Roman Elizarov99c28aa2018-09-23 18:42:36 +0300312<!--- MODULE kotlinx-coroutines-core -->
313<!--- INDEX kotlinx.coroutines.experimental -->
314[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/async.html
315[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/launch.html
316[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-job/index.html
317[Deferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/index.html
318[CoroutineStart.LAZY]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-coroutine-start/-l-a-z-y.html
319[Deferred.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/await.html
320[Job.start]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-job/start.html
321[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-global-scope/index.html
322[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-coroutine-scope/index.html
323[coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/coroutine-scope.html
324<!--- END -->