blob: 17f3dbb3062573c2653480e8962e1a054346c3b3 [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.
Roman Elizarov0950dfa2018-07-13 10:33:25 +03007package kotlinx.coroutines.guide.$$1$$2
hadihariri7db55532018-09-15 10:35:08 +02008-->
9<!--- KNIT ../core/kotlinx-coroutines-core/test/guide/.*\.kt -->
10<!--- TEST_OUT ../core/kotlinx-coroutines-core/test/guide/test/SelectGuideTest.kt
11// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
Roman Elizarov0950dfa2018-07-13 10:33:25 +030012package kotlinx.coroutines.guide.test
hadihariri7db55532018-09-15 10:35:08 +020013
14import org.junit.Test
15
16class SelectGuideTest {
17-->
18
19
20## Table of contents
21
22<!--- TOC -->
23
Vsevolod Tolstopyatovb590aa32018-09-27 18:34:05 +030024* [Select expression (experimental)](#select-expression-experimental)
hadihariri7db55532018-09-15 10:35:08 +020025 * [Selecting from channels](#selecting-from-channels)
26 * [Selecting on close](#selecting-on-close)
27 * [Selecting to send](#selecting-to-send)
28 * [Selecting deferred values](#selecting-deferred-values)
29 * [Switch over a channel of deferred values](#switch-over-a-channel-of-deferred-values)
30
31<!--- END_TOC -->
32
33
34
35## Select expression (experimental)
36
37Select expression makes it possible to await multiple suspending functions simultaneously and _select_
38the first one that becomes available.
39
40> Select expressions are an experimental feature of `kotlinx.coroutines`. Their API is expected to
41evolve in the upcoming updates of the `kotlinx.coroutines` library with potentially
42breaking changes.
43
44### Selecting from channels
45
46Let us have two producers of strings: `fizz` and `buzz`. The `fizz` produces "Fizz" string every 300 ms:
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
51fun CoroutineScope.fizz() = produce<String> {
52 while (true) { // sends "Fizz" every 300 ms
53 delay(300)
54 send("Fizz")
55 }
56}
57```
58
Alexander Prendotacbeef102018-09-27 18:42:04 +030059</div>
60
hadihariri7db55532018-09-15 10:35:08 +020061And the `buzz` produces "Buzz!" string every 500 ms:
62
Alexander Prendotacbeef102018-09-27 18:42:04 +030063<div class="sample" markdown="1" theme="idea" data-highlight-only>
64
hadihariri7db55532018-09-15 10:35:08 +020065```kotlin
66fun CoroutineScope.buzz() = produce<String> {
67 while (true) { // sends "Buzz!" every 500 ms
68 delay(500)
69 send("Buzz!")
70 }
71}
72```
73
Alexander Prendotacbeef102018-09-27 18:42:04 +030074</div>
75
hadihariri7db55532018-09-15 10:35:08 +020076Using [receive][ReceiveChannel.receive] suspending function we can receive _either_ from one channel or the
77other. But [select] expression allows us to receive from _both_ simultaneously using its
78[onReceive][ReceiveChannel.onReceive] clauses:
79
Alexander Prendotacbeef102018-09-27 18:42:04 +030080<div class="sample" markdown="1" theme="idea" data-highlight-only>
81
hadihariri7db55532018-09-15 10:35:08 +020082```kotlin
83suspend fun selectFizzBuzz(fizz: ReceiveChannel<String>, buzz: ReceiveChannel<String>) {
84 select<Unit> { // <Unit> means that this select expression does not produce any result
85 fizz.onReceive { value -> // this is the first select clause
86 println("fizz -> '$value'")
87 }
88 buzz.onReceive { value -> // this is the second select clause
89 println("buzz -> '$value'")
90 }
91 }
92}
93```
94
Alexander Prendotacbeef102018-09-27 18:42:04 +030095</div>
96
hadihariri7db55532018-09-15 10:35:08 +020097Let us run it all seven times:
98
Prendota0eee3c32018-10-22 12:52:56 +030099<!--- CLEAR -->
100
101<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300102
hadihariri7db55532018-09-15 10:35:08 +0200103```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300104import kotlinx.coroutines.*
105import kotlinx.coroutines.channels.*
106import kotlinx.coroutines.selects.*
107
108fun CoroutineScope.fizz() = produce<String> {
109 while (true) { // sends "Fizz" every 300 ms
110 delay(300)
111 send("Fizz")
112 }
113}
114
115fun CoroutineScope.buzz() = produce<String> {
116 while (true) { // sends "Buzz!" every 500 ms
117 delay(500)
118 send("Buzz!")
119 }
120}
121
122suspend fun selectFizzBuzz(fizz: ReceiveChannel<String>, buzz: ReceiveChannel<String>) {
123 select<Unit> { // <Unit> means that this select expression does not produce any result
124 fizz.onReceive { value -> // this is the first select clause
125 println("fizz -> '$value'")
126 }
127 buzz.onReceive { value -> // this is the second select clause
128 println("buzz -> '$value'")
129 }
130 }
131}
132
Prendota65e6c8c2018-10-17 11:51:08 +0300133fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +0300134//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200135 val fizz = fizz()
136 val buzz = buzz()
137 repeat(7) {
138 selectFizzBuzz(fizz, buzz)
139 }
Prendota0eee3c32018-10-22 12:52:56 +0300140 coroutineContext.cancelChildren() // cancel fizz & buzz coroutines
141//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200142}
143```
144
Alexander Prendotacbeef102018-09-27 18:42:04 +0300145</div>
146
hadihariri7db55532018-09-15 10:35:08 +0200147> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-select-01.kt)
148
149The result of this code is:
150
151```text
152fizz -> 'Fizz'
153buzz -> 'Buzz!'
154fizz -> 'Fizz'
155fizz -> 'Fizz'
156buzz -> 'Buzz!'
157fizz -> 'Fizz'
158buzz -> 'Buzz!'
159```
160
161<!--- TEST -->
162
163### Selecting on close
164
165The [onReceive][ReceiveChannel.onReceive] clause in `select` fails when the channel is closed causing the corresponding
166`select` to throw an exception. We can use [onReceiveOrNull][ReceiveChannel.onReceiveOrNull] clause to perform a
167specific action when the channel is closed. The following example also shows that `select` is an expression that returns
168the result of its selected clause:
169
Alexander Prendotacbeef102018-09-27 18:42:04 +0300170<div class="sample" markdown="1" theme="idea" data-highlight-only>
171
hadihariri7db55532018-09-15 10:35:08 +0200172```kotlin
173suspend fun selectAorB(a: ReceiveChannel<String>, b: ReceiveChannel<String>): String =
174 select<String> {
175 a.onReceiveOrNull { value ->
176 if (value == null)
177 "Channel 'a' is closed"
178 else
179 "a -> '$value'"
180 }
181 b.onReceiveOrNull { value ->
182 if (value == null)
183 "Channel 'b' is closed"
184 else
185 "b -> '$value'"
186 }
187 }
188```
189
Alexander Prendotacbeef102018-09-27 18:42:04 +0300190</div>
191
hadihariri7db55532018-09-15 10:35:08 +0200192Let's use it with channel `a` that produces "Hello" string four times and
193channel `b` that produces "World" four times:
194
Prendota0eee3c32018-10-22 12:52:56 +0300195<!--- CLEAR -->
196
197<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300198
hadihariri7db55532018-09-15 10:35:08 +0200199```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300200import kotlinx.coroutines.*
201import kotlinx.coroutines.channels.*
202import kotlinx.coroutines.selects.*
203
204suspend fun selectAorB(a: ReceiveChannel<String>, b: ReceiveChannel<String>): String =
205 select<String> {
206 a.onReceiveOrNull { value ->
207 if (value == null)
208 "Channel 'a' is closed"
209 else
210 "a -> '$value'"
211 }
212 b.onReceiveOrNull { value ->
213 if (value == null)
214 "Channel 'b' is closed"
215 else
216 "b -> '$value'"
217 }
218 }
219
Prendota65e6c8c2018-10-17 11:51:08 +0300220fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +0300221//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200222 val a = produce<String> {
223 repeat(4) { send("Hello $it") }
224 }
225 val b = produce<String> {
226 repeat(4) { send("World $it") }
227 }
228 repeat(8) { // print first eight results
229 println(selectAorB(a, b))
230 }
Prendota0eee3c32018-10-22 12:52:56 +0300231 coroutineContext.cancelChildren()
232//sampleEnd
233}
hadihariri7db55532018-09-15 10:35:08 +0200234```
235
Alexander Prendotacbeef102018-09-27 18:42:04 +0300236</div>
237
hadihariri7db55532018-09-15 10:35:08 +0200238> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-select-02.kt)
239
240The result of this code is quite interesting, so we'll analyze it in mode detail:
241
242```text
243a -> 'Hello 0'
244a -> 'Hello 1'
245b -> 'World 0'
246a -> 'Hello 2'
247a -> 'Hello 3'
248b -> 'World 1'
249Channel 'a' is closed
250Channel 'a' is closed
251```
252
253<!--- TEST -->
254
255There are couple of observations to make out of it.
256
257First of all, `select` is _biased_ to the first clause. When several clauses are selectable at the same time,
258the first one among them gets selected. Here, both channels are constantly producing strings, so `a` channel,
259being the first clause in select, wins. However, because we are using unbuffered channel, the `a` gets suspended from
260time to time on its [send][SendChannel.send] invocation and gives a chance for `b` to send, too.
261
262The second observation, is that [onReceiveOrNull][ReceiveChannel.onReceiveOrNull] gets immediately selected when the
263channel is already closed.
264
265### Selecting to send
266
267Select expression has [onSend][SendChannel.onSend] clause that can be used for a great good in combination
268with a biased nature of selection.
269
270Let us write an example of producer of integers that sends its values to a `side` channel when
271the consumers on its primary channel cannot keep up with it:
272
Alexander Prendotacbeef102018-09-27 18:42:04 +0300273<div class="sample" markdown="1" theme="idea" data-highlight-only>
274
hadihariri7db55532018-09-15 10:35:08 +0200275```kotlin
276fun CoroutineScope.produceNumbers(side: SendChannel<Int>) = produce<Int> {
277 for (num in 1..10) { // produce 10 numbers from 1 to 10
278 delay(100) // every 100 ms
279 select<Unit> {
280 onSend(num) {} // Send to the primary channel
281 side.onSend(num) {} // or to the side channel
282 }
283 }
284}
285```
286
Alexander Prendotacbeef102018-09-27 18:42:04 +0300287</div>
288
hadihariri7db55532018-09-15 10:35:08 +0200289Consumer is going to be quite slow, taking 250 ms to process each number:
290
Prendota0eee3c32018-10-22 12:52:56 +0300291<!--- CLEAR -->
292
293<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300294
hadihariri7db55532018-09-15 10:35:08 +0200295```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300296import kotlinx.coroutines.*
297import kotlinx.coroutines.channels.*
298import kotlinx.coroutines.selects.*
299
300fun CoroutineScope.produceNumbers(side: SendChannel<Int>) = produce<Int> {
301 for (num in 1..10) { // produce 10 numbers from 1 to 10
302 delay(100) // every 100 ms
303 select<Unit> {
304 onSend(num) {} // Send to the primary channel
305 side.onSend(num) {} // or to the side channel
306 }
307 }
308}
309
Prendota65e6c8c2018-10-17 11:51:08 +0300310fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +0300311//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200312 val side = Channel<Int>() // allocate side channel
313 launch { // this is a very fast consumer for the side channel
314 side.consumeEach { println("Side channel has $it") }
315 }
316 produceNumbers(side).consumeEach {
317 println("Consuming $it")
318 delay(250) // let us digest the consumed number properly, do not hurry
319 }
320 println("Done consuming")
Prendota0eee3c32018-10-22 12:52:56 +0300321 coroutineContext.cancelChildren()
322//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200323}
Alexander Prendotacbeef102018-09-27 18:42:04 +0300324```
325
326</div>
hadihariri7db55532018-09-15 10:35:08 +0200327
328> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-select-03.kt)
329
330So let us see what happens:
331
332```text
333Consuming 1
334Side channel has 2
335Side channel has 3
336Consuming 4
337Side channel has 5
338Side channel has 6
339Consuming 7
340Side channel has 8
341Side channel has 9
342Consuming 10
343Done consuming
344```
345
346<!--- TEST -->
347
348### Selecting deferred values
349
350Deferred values can be selected using [onAwait][Deferred.onAwait] clause.
351Let us start with an async function that returns a deferred string value after
352a random delay:
353
Alexander Prendotacbeef102018-09-27 18:42:04 +0300354<div class="sample" markdown="1" theme="idea" data-highlight-only>
355
hadihariri7db55532018-09-15 10:35:08 +0200356```kotlin
357fun CoroutineScope.asyncString(time: Int) = async {
358 delay(time.toLong())
359 "Waited for $time ms"
360}
361```
362
Alexander Prendotacbeef102018-09-27 18:42:04 +0300363</div>
364
hadihariri7db55532018-09-15 10:35:08 +0200365Let us start a dozen of them with a random delay.
366
Alexander Prendotacbeef102018-09-27 18:42:04 +0300367<div class="sample" markdown="1" theme="idea" data-highlight-only>
368
hadihariri7db55532018-09-15 10:35:08 +0200369```kotlin
370fun CoroutineScope.asyncStringsList(): List<Deferred<String>> {
371 val random = Random(3)
372 return List(12) { asyncString(random.nextInt(1000)) }
373}
374```
375
Alexander Prendotacbeef102018-09-27 18:42:04 +0300376</div>
377
hadihariri7db55532018-09-15 10:35:08 +0200378Now the main function awaits for the first of them to complete and counts the number of deferred values
379that are still active. Note, that we've used here the fact that `select` expression is a Kotlin DSL,
380so we can provide clauses for it using an arbitrary code. In this case we iterate over a list
381of deferred values to provide `onAwait` clause for each deferred value.
382
Prendota0eee3c32018-10-22 12:52:56 +0300383<!--- CLEAR -->
384
385<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300386
hadihariri7db55532018-09-15 10:35:08 +0200387```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300388import kotlinx.coroutines.*
389import kotlinx.coroutines.selects.*
390import java.util.*
391
392fun CoroutineScope.asyncString(time: Int) = async {
393 delay(time.toLong())
394 "Waited for $time ms"
395}
396
397fun CoroutineScope.asyncStringsList(): List<Deferred<String>> {
398 val random = Random(3)
399 return List(12) { asyncString(random.nextInt(1000)) }
400}
401
Prendota65e6c8c2018-10-17 11:51:08 +0300402fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +0300403//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200404 val list = asyncStringsList()
405 val result = select<String> {
406 list.withIndex().forEach { (index, deferred) ->
407 deferred.onAwait { answer ->
408 "Deferred $index produced answer '$answer'"
409 }
410 }
411 }
412 println(result)
413 val countActive = list.count { it.isActive }
414 println("$countActive coroutines are still active")
Prendota0eee3c32018-10-22 12:52:56 +0300415//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200416}
417```
418
Alexander Prendotacbeef102018-09-27 18:42:04 +0300419</div>
420
hadihariri7db55532018-09-15 10:35:08 +0200421> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-select-04.kt)
422
423The output is:
424
425```text
426Deferred 4 produced answer 'Waited for 128 ms'
42711 coroutines are still active
428```
429
430<!--- TEST -->
431
432### Switch over a channel of deferred values
433
434Let us write a channel producer function that consumes a channel of deferred string values, waits for each received
435deferred value, but only until the next deferred value comes over or the channel is closed. This example puts together
436[onReceiveOrNull][ReceiveChannel.onReceiveOrNull] and [onAwait][Deferred.onAwait] clauses in the same `select`:
437
Alexander Prendotacbeef102018-09-27 18:42:04 +0300438<div class="sample" markdown="1" theme="idea" data-highlight-only>
439
hadihariri7db55532018-09-15 10:35:08 +0200440```kotlin
441fun CoroutineScope.switchMapDeferreds(input: ReceiveChannel<Deferred<String>>) = produce<String> {
442 var current = input.receive() // start with first received deferred value
443 while (isActive) { // loop while not cancelled/closed
444 val next = select<Deferred<String>?> { // return next deferred value from this select or null
445 input.onReceiveOrNull { update ->
446 update // replaces next value to wait
447 }
448 current.onAwait { value ->
449 send(value) // send value that current deferred has produced
450 input.receiveOrNull() // and use the next deferred from the input channel
451 }
452 }
453 if (next == null) {
454 println("Channel was closed")
455 break // out of loop
456 } else {
457 current = next
458 }
459 }
460}
461```
462
Alexander Prendotacbeef102018-09-27 18:42:04 +0300463</div>
464
hadihariri7db55532018-09-15 10:35:08 +0200465To test it, we'll use a simple async function that resolves to a specified string after a specified time:
466
Alexander Prendotacbeef102018-09-27 18:42:04 +0300467
468<div class="sample" markdown="1" theme="idea" data-highlight-only>
469
hadihariri7db55532018-09-15 10:35:08 +0200470```kotlin
471fun CoroutineScope.asyncString(str: String, time: Long) = async {
472 delay(time)
473 str
474}
475```
476
Alexander Prendotacbeef102018-09-27 18:42:04 +0300477</div>
478
hadihariri7db55532018-09-15 10:35:08 +0200479The main function just launches a coroutine to print results of `switchMapDeferreds` and sends some test
480data to it:
481
Prendota0eee3c32018-10-22 12:52:56 +0300482<!--- CLEAR -->
483
484<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300485
hadihariri7db55532018-09-15 10:35:08 +0200486```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300487import kotlinx.coroutines.*
488import kotlinx.coroutines.channels.*
489import kotlinx.coroutines.selects.*
490
491fun CoroutineScope.switchMapDeferreds(input: ReceiveChannel<Deferred<String>>) = produce<String> {
492 var current = input.receive() // start with first received deferred value
493 while (isActive) { // loop while not cancelled/closed
494 val next = select<Deferred<String>?> { // return next deferred value from this select or null
495 input.onReceiveOrNull { update ->
496 update // replaces next value to wait
497 }
498 current.onAwait { value ->
499 send(value) // send value that current deferred has produced
500 input.receiveOrNull() // and use the next deferred from the input channel
501 }
502 }
503 if (next == null) {
504 println("Channel was closed")
505 break // out of loop
506 } else {
507 current = next
508 }
509 }
510}
511
512fun CoroutineScope.asyncString(str: String, time: Long) = async {
513 delay(time)
514 str
515}
516
Prendota65e6c8c2018-10-17 11:51:08 +0300517fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +0300518//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200519 val chan = Channel<Deferred<String>>() // the channel for test
520 launch { // launch printing coroutine
521 for (s in switchMapDeferreds(chan))
522 println(s) // print each received string
523 }
524 chan.send(asyncString("BEGIN", 100))
525 delay(200) // enough time for "BEGIN" to be produced
526 chan.send(asyncString("Slow", 500))
527 delay(100) // not enough time to produce slow
528 chan.send(asyncString("Replace", 100))
529 delay(500) // give it time before the last one
530 chan.send(asyncString("END", 500))
531 delay(1000) // give it time to process
532 chan.close() // close the channel ...
533 delay(500) // and wait some time to let it finish
Prendota0eee3c32018-10-22 12:52:56 +0300534//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200535}
536```
537
Alexander Prendotacbeef102018-09-27 18:42:04 +0300538</div>
539
hadihariri7db55532018-09-15 10:35:08 +0200540> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-select-05.kt)
541
542The result of this code:
543
544```text
545BEGIN
546Replace
547END
548Channel was closed
549```
550
551<!--- TEST -->
552
553<!--- MODULE kotlinx-coroutines-core -->
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300554<!--- INDEX kotlinx.coroutines -->
555[Deferred.onAwait]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/on-await.html
556<!--- INDEX kotlinx.coroutines.channels -->
557[ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html
558[ReceiveChannel.onReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive.html
559[ReceiveChannel.onReceiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive-or-null.html
560[SendChannel.send]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/send.html
561[SendChannel.onSend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/on-send.html
562<!--- INDEX kotlinx.coroutines.selects -->
563[select]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.selects/select.html
hadihariri7db55532018-09-15 10:35:08 +0200564<!--- END -->