blob: b8ac6c7beeee6001da9926b4c098d3ba87138209 [file] [log] [blame] [view]
Roman Elizarov7deefb82017-01-31 10:33:17 +03001# Guide to kotlinx.coroutines by example
2
3This is a short guide on core features of `kotlinx.coroutines` with a series of examples.
4
Roman Elizarov1293ccd2017-02-01 18:49:54 +03005## Table of contents
6
7* [Coroutine basics](#coroutine-basics)
8 * [Your first coroutine](#your-first-coroutine)
9 * [Bridging blocking and non-blocking worlds](#bridging-blocking-and-non-blocking-worlds)
10 * [Waiting for a job](#waiting-for-a-job)
11 * [Extract function refactoring](#extract-function-refactoring)
12 * [Coroutines ARE light-weight](#coroutines-are-light-weight)
13 * [Coroutines are like daemon threads](#coroutines-are-like-daemon-threads)
14* [Cancellation and timeouts](#cancellation-and-timeouts)
15 * [Cancelling coroutine execution](#cancelling-coroutine-execution)
16 * [Cancellation is cooperative](#cancellation-is-cooperative)
17 * [Making computation code cancellable](#making-computation-code-cancellable)
18 * [Closing resources with finally](#closing-resources-with-finally)
19 * [Run non-cancellable block](#run-non-cancellable-block)
20 * [Timeout](#timeout)
21* [Composing suspending functions](#composing-suspending-functions)
22 * [Sequential by default](#sequential-by-default)
23 * [Concurrent using deferred value](#concurrent-using-deferred-value)
24 * [Lazily deferred value](#lazily-deferred-value)
25
26## Coroutine basics
27
28This section covers basic coroutine concepts.
29
30### Your first coroutine
Roman Elizarov7deefb82017-01-31 10:33:17 +030031
32Run the following code:
33
34```kotlin
35fun main(args: Array<String>) {
36 launch(CommonPool) { // create new coroutine in common thread pool
37 delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
38 println("World!") // print after delay
39 }
40 println("Hello,") // main function continues while coroutine is delayed
41 Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
42}
43```
44
Roman Elizarov1293ccd2017-02-01 18:49:54 +030045> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-11.kt)
Roman Elizarov7deefb82017-01-31 10:33:17 +030046
47Run this code:
48
49```
50Hello,
51World!
52```
53
54Essentially, coroutines are light-weight threads. You can achieve the same result replacing
55`launch(CommonPool) { ... }` with `thread { ... }` and `delay(...)` with `Thread.sleep(...)`. Try it.
56
57If you start by replacing `launch(CommonPool)` by `thread`, the compiler produces the following error:
58
59```
60Error: Kotlin: Suspend functions are only allowed to be called from a coroutine or another suspend function
61```
62
63That is because `delay` is a special _suspending function_ that does not block a thread, but _suspends_
64coroutine and it can be only used from a coroutine.
65
Roman Elizarov1293ccd2017-02-01 18:49:54 +030066### Bridging blocking and non-blocking worlds
Roman Elizarov7deefb82017-01-31 10:33:17 +030067
68The first example mixes _non-blocking_ `delay(...)` and _blocking_ `Thread.sleep(...)` in the same
69code of `main` function. It is easy to get lost. Let's cleanly separate blocking and non-blocking
70worlds by using `runBlocking { ... }`:
71
72```kotlin
73fun main(args: Array<String>) = runBlocking<Unit> { // start main coroutine
74 launch(CommonPool) { // create new coroutine in common thread pool
75 delay(1000L)
76 println("World!")
77 }
78 println("Hello,") // main coroutine continues while child is delayed
79 delay(2000L) // non-blocking delay for 2 seconds to keep JVM alive
80}
81```
82
Roman Elizarov1293ccd2017-02-01 18:49:54 +030083> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-12.kt)
Roman Elizarov7deefb82017-01-31 10:33:17 +030084
85The result is the same, but this code uses only non-blocking `delay`.
86
87`runBlocking { ... }` works as an adaptor that is used here to start the top-level main coroutine.
88The regular code outside of `runBlocking` _blocks_, until the coroutine inside `runBlocking` is active.
89
90This is also a way to write unit-tests for suspending functions:
91
92```kotlin
93class MyTest {
94 @Test
95 fun testMySuspendingFunction() = runBlocking<Unit> {
96 // here we can use suspending functions using any assertion style that we like
97 }
98}
99```
100
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300101### Waiting for a job
Roman Elizarov7deefb82017-01-31 10:33:17 +0300102
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300103Delaying for a time while another coroutine is working is not a good approach. Let's explicitly
104wait (in a non-blocking way) until the background job coroutine that we have launched is complete:
Roman Elizarov7deefb82017-01-31 10:33:17 +0300105
106```kotlin
107fun main(args: Array<String>) = runBlocking<Unit> {
108 val job = launch(CommonPool) { // create new coroutine and keep a reference to its Job
109 delay(1000L)
110 println("World!")
111 }
112 println("Hello,")
113 job.join() // wait until child coroutine completes
114}
115```
116
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300117> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-13.kt)
Roman Elizarov7deefb82017-01-31 10:33:17 +0300118
119Now the result is still the same, but the code of the main coroutine is not tied to the duration of
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300120the background job in any way. Much better.
Roman Elizarov7deefb82017-01-31 10:33:17 +0300121
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300122### Extract function refactoring
Roman Elizarov7deefb82017-01-31 10:33:17 +0300123
124Let's extract the block of code inside `launch(CommonPool} { ... }` into a separate function. When you
125perform "Extract function" refactoring on this code you get a new function with `suspend` modifier.
126That is your first _suspending function_. Suspending functions can be used inside coroutines
127just like regular functions, but their additional feature is that they can, in turn,
128use other suspending functions, like `delay` in this example, to _suspend_ execution of a coroutine.
129
130```kotlin
131fun main(args: Array<String>) = runBlocking<Unit> {
132 val job = launch(CommonPool) { doWorld() }
133 println("Hello,")
134 job.join()
135}
136
137// this is your first suspending function
138suspend fun doWorld() {
139 delay(1000L)
140 println("World!")
141}
142```
143
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300144> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-14.kt)
Roman Elizarov7deefb82017-01-31 10:33:17 +0300145
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300146### Coroutines ARE light-weight
Roman Elizarov7deefb82017-01-31 10:33:17 +0300147
148Run the following code:
149
150```kotlin
151fun main(args: Array<String>) = runBlocking<Unit> {
152 val jobs = List(100_000) { // create a lot of coroutines and list their jobs
153 launch(CommonPool) {
154 delay(1000L)
155 print(".")
156 }
157 }
158 jobs.forEach { it.join() } // wait for all jobs to complete
159}
160```
161
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300162> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-15.kt)
Roman Elizarov7deefb82017-01-31 10:33:17 +0300163
164It starts 100K coroutines and, after a second, each coroutine prints a dot.
165Now, try that with threads. What would happen? (Most likely your code will produce some sort of out-of-memory error)
166
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300167### Coroutines are like daemon threads
Roman Elizarov7deefb82017-01-31 10:33:17 +0300168
169The following code launches a long-running coroutine that prints "I'm sleeping" twice a second and then
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300170returns from the main function after some delay:
Roman Elizarov7deefb82017-01-31 10:33:17 +0300171
172```kotlin
173fun main(args: Array<String>) = runBlocking<Unit> {
174 launch(CommonPool) {
175 repeat(1000) { i ->
176 println("I'm sleeping $i ...")
177 delay(500L)
178 }
179 }
180 delay(1300L) // just quit after delay
181}
182```
183
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300184> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-16.kt)
Roman Elizarov7deefb82017-01-31 10:33:17 +0300185
186You can run and see that it prints three lines and terminates:
187
188```
189I'm sleeping 0 ...
190I'm sleeping 1 ...
191I'm sleeping 2 ...
192```
193
194Active coroutines do not keep the process alive. They are like daemon threads.
195
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300196## Cancellation and timeouts
197
198This section covers coroutine cancellation and timeouts.
199
200### Cancelling coroutine execution
Roman Elizarov7deefb82017-01-31 10:33:17 +0300201
202In small application the return from "main" method might sound like a good idea to get all coroutines
203implicitly terminated. In a larger, long-running application, you need finer-grained control.
204The `launch` function returns a `Job` that can be used to cancel running coroutine:
205
206```kotlin
207fun main(args: Array<String>) = runBlocking<Unit> {
208 val job = launch(CommonPool) {
209 repeat(1000) { i ->
210 println("I'm sleeping $i ...")
211 delay(500L)
212 }
213 }
214 delay(1300L) // delay a bit
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300215 println("main: I'm tired of waiting!")
Roman Elizarov7deefb82017-01-31 10:33:17 +0300216 job.cancel() // cancels the job
217 delay(1300L) // delay a bit to ensure it was cancelled indeed
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300218 println("main: Now I can quit.")
Roman Elizarov7deefb82017-01-31 10:33:17 +0300219}
220```
221
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300222> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-21.kt)
Roman Elizarov7deefb82017-01-31 10:33:17 +0300223
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300224It produces the following output:
225
226```
227I'm sleeping 0 ...
228I'm sleeping 1 ...
229I'm sleeping 2 ...
230main: I'm tired of waiting!
231main: Now I can quit.
232```
233
234As soon as main invokes `job.cancel`, we don't see any output from the other coroutine because it was cancelled.
235
236### Cancellation is cooperative
Roman Elizarov7deefb82017-01-31 10:33:17 +0300237
Tair Rzayevaf734622017-02-01 22:30:16 +0200238Coroutine cancellation is _cooperative_. A coroutine code has to cooperate to be cancellable.
Roman Elizarov7deefb82017-01-31 10:33:17 +0300239All the suspending functions in `kotlinx.coroutines` are _cancellable_. They check for cancellation of
240coroutine and throw `CancellationException` when cancelled. However, if a coroutine is working in
241a computation and does not check for cancellation, then it cannot be cancelled, like the following
242example shows:
243
244```kotlin
245fun main(args: Array<String>) = runBlocking<Unit> {
246 val job = launch(CommonPool) {
247 var nextPrintTime = 0L
248 var i = 0
249 while (true) { // computation loop
250 val currentTime = System.currentTimeMillis()
251 if (currentTime >= nextPrintTime) {
252 println("I'm sleeping ${i++} ...")
253 nextPrintTime = currentTime + 500L
254 }
255 }
256 }
257 delay(1300L) // delay a bit
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300258 println("main: I'm tired of waiting!")
Roman Elizarov7deefb82017-01-31 10:33:17 +0300259 job.cancel() // cancels the job
260 delay(1300L) // delay a bit to see if it was cancelled....
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300261 println("main: Now I can quit.")
Roman Elizarov7deefb82017-01-31 10:33:17 +0300262}
263```
264
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300265> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-22.kt)
Roman Elizarov7deefb82017-01-31 10:33:17 +0300266
267Run it to see that it continues to print "I'm sleeping" even after cancellation.
268
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300269### Making computation code cancellable
Roman Elizarov7deefb82017-01-31 10:33:17 +0300270
271There are two approaches to making computation code cancellable. The first one is to periodically
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300272invoke a suspending function. There is a `yield` function that is a good choice for that purpose.
273The other one is to explicitly check the cancellation status. Let us try the later approach.
Roman Elizarov7deefb82017-01-31 10:33:17 +0300274
275Replace `while (true)` in the previous example with `while (isActive)` and rerun it.
276
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300277> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-23.kt)
Roman Elizarov7deefb82017-01-31 10:33:17 +0300278
279As you can see, now this loop can be cancelled. `isActive` is a property that is available inside
280the code of coroutines via `CoroutineScope` object.
281
Roman Elizarov1293ccd2017-02-01 18:49:54 +0300282### Closing resources with finally
283
284Cancellable suspending functions throw `CancellationException` on cancellation which can be handled in
285all the usual way. For example, the `try {...} finally {...}` and Kotlin `use` function execute their
286finalization actions normally when coroutine is cancelled:
287
288```kotlin
289fun main(args: Array<String>) = runBlocking<Unit> {
290 val job = launch(CommonPool) {
291 try {
292 repeat(1000) { i ->
293 println("I'm sleeping $i ...")
294 delay(500L)
295 }
296 } finally {
297 println("I'm running finally")
298 }
299 }
300 delay(1300L) // delay a bit
301 println("main: I'm tired of waiting!")
302 job.cancel() // cancels the job
303 delay(1300L) // delay a bit to ensure it was cancelled indeed
304 println("main: Now I can quit.")
305}
306```
307
308> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-24.kt)
309
310The example above produces the following output:
311
312```
313I'm sleeping 0 ...
314I'm sleeping 1 ...
315I'm sleeping 2 ...
316main: I'm tired of waiting!
317I'm running finally
318main: Now I can quit.
319```
320
321### Run non-cancellable block
322
323Any attempt to use a suspending function in the `finally` block of the previous example will cause
324`CancellationException`, because the coroutine running this code is cancelled. Usually, this is not a
325problem, since all well-behaving closing operations (closing a file, cancelling a job, or closing any kind of a
326communication channel) are usually non-blocking and do not involve any suspending functions. However, in the
327rare case when you need to suspend in the cancelled coroutine you can wrap the corresponding code in
328`run(NonCancellable) {...}` as the following example shows:
329
330```kotlin
331fun main(args: Array<String>) = runBlocking<Unit> {
332 val job = launch(CommonPool) {
333 try {
334 repeat(1000) { i ->
335 println("I'm sleeping $i ...")
336 delay(500L)
337 }
338 } finally {
339 run(NonCancellable) {
340 println("I'm running finally")
341 delay(1000L)
342 println("And I've just delayed for 1 sec because I'm non-cancellable")
343 }
344 }
345 }
346 delay(1300L) // delay a bit
347 println("main: I'm tired of waiting!")
348 job.cancel() // cancels the job
349 delay(1300L) // delay a bit to ensure it was cancelled indeed
350 println("main: Now I can quit.")
351}
352```
353
354> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-25.kt)
355
356### Timeout
357
358The most obvious reason to cancel coroutine execution in practice,
359is because its execution time has exceeded some timeout.
360While you can manually track the reference to the corresponding `job` and launch a separate coroutine to cancel
361the tracked one after delay, there is a ready to use `withTimeout(...) {...}` function that does it.
362Look at the following example:
363
364```kotlin
365fun main(args: Array<String>) = runBlocking<Unit> {
366 withTimeout(1300L) {
367 repeat(1000) { i ->
368 println("I'm sleeping $i ...")
369 delay(500L)
370 }
371 }
372}
373```
374
375> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-26.kt)
376
377It produces the following output:
378
379```
380I'm sleeping 0 ...
381I'm sleeping 1 ...
382I'm sleeping 2 ...
383Exception in thread "main" java.util.concurrent.CancellationException: Timed out waiting for 1300 MILLISECONDS
384```
385
386We have not seen the `CancellationException` stack trace printed on the console before. That is because
387inside a cancelled coroutine `CancellationException` is a considered a normal reason for coroutine completion.
388However, in this example we have used `withTimeout` right inside the `main` function.
389
390Because cancellation is just an exception, all the resources will be closed in a usual way.
391You can wrap the code with timeout in `try {...} catch (e: CancellationException) {...}` block if
392you need to do some additional action specifically on timeout.
393
394## Composing suspending functions
395
396This section covers various approaches to composition of suspending functions.
397
398### Sequential by default
399
400Assume that we have two suspending functions defined elsewhere that do something useful like some kind of
401remote service call or computation. We'll just pretend they are useful, but each one will just actaully
402delay for a second for the purpose of this example:
403
404```kotlin
405suspend fun doSomethingUsefulOne(): Int {
406 delay(1000L) // pretend we are doing something useful here
407 return 13
408}
409
410suspend fun doSomethingUsefulTwo(): Int {
411 delay(1000L) // pretend we are doing something useful here, too
412 return 29
413}
414```
415
416What do we do if need to invoke them _sequentially_ -- first `doSomethingUsefulOne` _and then_
417`doSomethingUsefulTwo` and compute the sum of their results?
418In practise we do this if we use the results of the first function to make a decision on whether we need
419to invoke the second one or to decide on how to invoke it.
420
421We just use a normal sequential invocation, because the code in the coroutine, just like in the regular
422code, is _sequential_ by default. The following example demonstrates that by measuring the total
423time it takes to execute both suspending functions:
424
425```kotlin
426fun main(args: Array<String>) = runBlocking<Unit> {
427 val time = measureTimeMillis {
428 val one = doSomethingUsefulOne()
429 val two = doSomethingUsefulTwo()
430 println("The answer is ${one + two}")
431 }
432 println("Completed in $time ms")
433}
434```
435
436> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-31.kt)
437
438It produces something like this:
439
440```
441The answer is 42
442Completed in 2017 ms
443```
444
445### Concurrent using deferred value
446
447What if there are no dependencies between invocation of `doSomethingUsefulOne` and `doSomethingUsefulTwo` and
448we want to get the answer faster, by doing both _concurrently_? This is where `defer` comes to helps.
449
450Conceptually, `defer` is just like `launch`. It starts a separate coroutine which is a light-weight thread
451that works concurrently with all the other coroutines. The difference is that `launch` returns a `Job` and
452does not carry any resulting value, while `defer` returns a `Deferred` -- a kind of light-weight non-blocking future
453that represent a promise to provide result later. You can use `.await()` on a deferred value to get its eventual result,
454but `Deferred` is also a `Job`, so you can cancel it if needed.
455
456```kotlin
457fun main(args: Array<String>) = runBlocking<Unit> {
458 val time = measureTimeMillis {
459 val one = defer(CommonPool) { doSomethingUsefulOne() }
460 val two = defer(CommonPool) { doSomethingUsefulTwo() }
461 println("The answer is ${one.await() + two.await()}")
462 }
463 println("Completed in $time ms")
464}
465```
466
467> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-32.kt)
468
469It produces something like this:
470
471```
472The answer is 42
473Completed in 1017 ms
474```
475
476This is twice as fast, because we have concurrent execution of two coroutines.
477Note, that concurrency with coroutines is always explicit.
478
479### Lazily deferred value
480
481There is a lazy alternative to `defer` that is called `lazyDefer`. It is just like `defer`, but it
482starts coroutine only when its result is needed by some `await` or if a special `start` function
483is invoked. Run the following example:
484
485```kotlin
486fun main(args: Array<String>) = runBlocking<Unit> {
487 val time = measureTimeMillis {
488 val one = lazyDefer(CommonPool) { doSomethingUsefulOne() }
489 val two = lazyDefer(CommonPool) { doSomethingUsefulTwo() }
490 println("The answer is ${one.await() + two.await()}")
491 }
492 println("Completed in $time ms")
493}
494```
495
496> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-33.kt)
497
498It produces something like this:
499
500```
501The answer is 42
502Completed in 2017 ms
503```
504
505So, we are back to two sequential execution, because we _first_ await for the `one` deferred, _and then_ await
506for the second one. It is not the intended use-case for `lazyDefer`. It is designed as a replacement for
507the standard `lazy` function in cases when computation of the value involve suspending functions.
508
509
Roman Elizarov7deefb82017-01-31 10:33:17 +0300510