blob: 842ab3a2b571ace11295e5ba5b9831f91b3db7a0 [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
Roman Elizarov0950dfa2018-07-13 10:33:25 +03009import kotlinx.coroutines.*
10import kotlinx.coroutines.channels.*
11import kotlinx.coroutines.selects.*
hadihariri7db55532018-09-15 10:35:08 +020012-->
13<!--- KNIT ../core/kotlinx-coroutines-core/test/guide/.*\.kt -->
14<!--- TEST_OUT ../core/kotlinx-coroutines-core/test/guide/test/SelectGuideTest.kt
15// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit.
Roman Elizarov0950dfa2018-07-13 10:33:25 +030016package kotlinx.coroutines.guide.test
hadihariri7db55532018-09-15 10:35:08 +020017
18import org.junit.Test
19
20class SelectGuideTest {
21-->
22
23
24## Table of contents
25
26<!--- TOC -->
27
Vsevolod Tolstopyatovb590aa32018-09-27 18:34:05 +030028* [Select expression (experimental)](#select-expression-experimental)
hadihariri7db55532018-09-15 10:35:08 +020029 * [Selecting from channels](#selecting-from-channels)
30 * [Selecting on close](#selecting-on-close)
31 * [Selecting to send](#selecting-to-send)
32 * [Selecting deferred values](#selecting-deferred-values)
33 * [Switch over a channel of deferred values](#switch-over-a-channel-of-deferred-values)
34
35<!--- END_TOC -->
36
37
38
39## Select expression (experimental)
40
41Select expression makes it possible to await multiple suspending functions simultaneously and _select_
42the first one that becomes available.
43
44> Select expressions are an experimental feature of `kotlinx.coroutines`. Their API is expected to
45evolve in the upcoming updates of the `kotlinx.coroutines` library with potentially
46breaking changes.
47
48### Selecting from channels
49
50Let us have two producers of strings: `fizz` and `buzz`. The `fizz` produces "Fizz" string every 300 ms:
51
52<!--- INCLUDE
Roman Elizarov0950dfa2018-07-13 10:33:25 +030053import kotlinx.coroutines.*
54import kotlin.coroutines.*
hadihariri7db55532018-09-15 10:35:08 +020055-->
56
Alexander Prendotacbeef102018-09-27 18:42:04 +030057<div class="sample" markdown="1" theme="idea" data-highlight-only>
58
hadihariri7db55532018-09-15 10:35:08 +020059```kotlin
60fun CoroutineScope.fizz() = produce<String> {
61 while (true) { // sends "Fizz" every 300 ms
62 delay(300)
63 send("Fizz")
64 }
65}
66```
67
Alexander Prendotacbeef102018-09-27 18:42:04 +030068</div>
69
hadihariri7db55532018-09-15 10:35:08 +020070And the `buzz` produces "Buzz!" string every 500 ms:
71
Alexander Prendotacbeef102018-09-27 18:42:04 +030072<div class="sample" markdown="1" theme="idea" data-highlight-only>
73
hadihariri7db55532018-09-15 10:35:08 +020074```kotlin
75fun CoroutineScope.buzz() = produce<String> {
76 while (true) { // sends "Buzz!" every 500 ms
77 delay(500)
78 send("Buzz!")
79 }
80}
81```
82
Alexander Prendotacbeef102018-09-27 18:42:04 +030083</div>
84
hadihariri7db55532018-09-15 10:35:08 +020085Using [receive][ReceiveChannel.receive] suspending function we can receive _either_ from one channel or the
86other. But [select] expression allows us to receive from _both_ simultaneously using its
87[onReceive][ReceiveChannel.onReceive] clauses:
88
Alexander Prendotacbeef102018-09-27 18:42:04 +030089<div class="sample" markdown="1" theme="idea" data-highlight-only>
90
hadihariri7db55532018-09-15 10:35:08 +020091```kotlin
92suspend fun selectFizzBuzz(fizz: ReceiveChannel<String>, buzz: ReceiveChannel<String>) {
93 select<Unit> { // <Unit> means that this select expression does not produce any result
94 fizz.onReceive { value -> // this is the first select clause
95 println("fizz -> '$value'")
96 }
97 buzz.onReceive { value -> // this is the second select clause
98 println("buzz -> '$value'")
99 }
100 }
101}
102```
103
Alexander Prendotacbeef102018-09-27 18:42:04 +0300104</div>
105
hadihariri7db55532018-09-15 10:35:08 +0200106Let us run it all seven times:
107
Alexander Prendotacbeef102018-09-27 18:42:04 +0300108<div class="sample" markdown="1" theme="idea" data-highlight-only>
109
hadihariri7db55532018-09-15 10:35:08 +0200110```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300111fun main() = runBlocking<Unit> {
hadihariri7db55532018-09-15 10:35:08 +0200112 val fizz = fizz()
113 val buzz = buzz()
114 repeat(7) {
115 selectFizzBuzz(fizz, buzz)
116 }
117 coroutineContext.cancelChildren() // cancel fizz & buzz coroutines
118}
119```
120
Alexander Prendotacbeef102018-09-27 18:42:04 +0300121</div>
122
hadihariri7db55532018-09-15 10:35:08 +0200123> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-select-01.kt)
124
125The result of this code is:
126
127```text
128fizz -> 'Fizz'
129buzz -> 'Buzz!'
130fizz -> 'Fizz'
131fizz -> 'Fizz'
132buzz -> 'Buzz!'
133fizz -> 'Fizz'
134buzz -> 'Buzz!'
135```
136
137<!--- TEST -->
138
139### Selecting on close
140
141The [onReceive][ReceiveChannel.onReceive] clause in `select` fails when the channel is closed causing the corresponding
142`select` to throw an exception. We can use [onReceiveOrNull][ReceiveChannel.onReceiveOrNull] clause to perform a
143specific action when the channel is closed. The following example also shows that `select` is an expression that returns
144the result of its selected clause:
145
146<!--- INCLUDE
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300147import kotlin.coroutines.*
hadihariri7db55532018-09-15 10:35:08 +0200148-->
149
Alexander Prendotacbeef102018-09-27 18:42:04 +0300150<div class="sample" markdown="1" theme="idea" data-highlight-only>
151
hadihariri7db55532018-09-15 10:35:08 +0200152```kotlin
153suspend fun selectAorB(a: ReceiveChannel<String>, b: ReceiveChannel<String>): String =
154 select<String> {
155 a.onReceiveOrNull { value ->
156 if (value == null)
157 "Channel 'a' is closed"
158 else
159 "a -> '$value'"
160 }
161 b.onReceiveOrNull { value ->
162 if (value == null)
163 "Channel 'b' is closed"
164 else
165 "b -> '$value'"
166 }
167 }
168```
169
Alexander Prendotacbeef102018-09-27 18:42:04 +0300170</div>
171
hadihariri7db55532018-09-15 10:35:08 +0200172Let's use it with channel `a` that produces "Hello" string four times and
173channel `b` that produces "World" four times:
174
Alexander Prendotacbeef102018-09-27 18:42:04 +0300175<div class="sample" markdown="1" theme="idea" data-highlight-only>
176
hadihariri7db55532018-09-15 10:35:08 +0200177```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300178fun main() = runBlocking<Unit> {
hadihariri7db55532018-09-15 10:35:08 +0200179 val a = produce<String> {
180 repeat(4) { send("Hello $it") }
181 }
182 val b = produce<String> {
183 repeat(4) { send("World $it") }
184 }
185 repeat(8) { // print first eight results
186 println(selectAorB(a, b))
187 }
188 coroutineContext.cancelChildren()
189}
190```
191
Alexander Prendotacbeef102018-09-27 18:42:04 +0300192</div>
193
hadihariri7db55532018-09-15 10:35:08 +0200194> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-select-02.kt)
195
196The result of this code is quite interesting, so we'll analyze it in mode detail:
197
198```text
199a -> 'Hello 0'
200a -> 'Hello 1'
201b -> 'World 0'
202a -> 'Hello 2'
203a -> 'Hello 3'
204b -> 'World 1'
205Channel 'a' is closed
206Channel 'a' is closed
207```
208
209<!--- TEST -->
210
211There are couple of observations to make out of it.
212
213First of all, `select` is _biased_ to the first clause. When several clauses are selectable at the same time,
214the first one among them gets selected. Here, both channels are constantly producing strings, so `a` channel,
215being the first clause in select, wins. However, because we are using unbuffered channel, the `a` gets suspended from
216time to time on its [send][SendChannel.send] invocation and gives a chance for `b` to send, too.
217
218The second observation, is that [onReceiveOrNull][ReceiveChannel.onReceiveOrNull] gets immediately selected when the
219channel is already closed.
220
221### Selecting to send
222
223Select expression has [onSend][SendChannel.onSend] clause that can be used for a great good in combination
224with a biased nature of selection.
225
226Let us write an example of producer of integers that sends its values to a `side` channel when
227the consumers on its primary channel cannot keep up with it:
228
229<!--- INCLUDE
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300230import kotlin.coroutines.*
hadihariri7db55532018-09-15 10:35:08 +0200231-->
232
Alexander Prendotacbeef102018-09-27 18:42:04 +0300233<div class="sample" markdown="1" theme="idea" data-highlight-only>
234
hadihariri7db55532018-09-15 10:35:08 +0200235```kotlin
236fun CoroutineScope.produceNumbers(side: SendChannel<Int>) = produce<Int> {
237 for (num in 1..10) { // produce 10 numbers from 1 to 10
238 delay(100) // every 100 ms
239 select<Unit> {
240 onSend(num) {} // Send to the primary channel
241 side.onSend(num) {} // or to the side channel
242 }
243 }
244}
245```
246
Alexander Prendotacbeef102018-09-27 18:42:04 +0300247</div>
248
hadihariri7db55532018-09-15 10:35:08 +0200249Consumer is going to be quite slow, taking 250 ms to process each number:
250
Alexander Prendotacbeef102018-09-27 18:42:04 +0300251<div class="sample" markdown="1" theme="idea" data-highlight-only>
252
hadihariri7db55532018-09-15 10:35:08 +0200253```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300254fun main() = runBlocking<Unit> {
hadihariri7db55532018-09-15 10:35:08 +0200255 val side = Channel<Int>() // allocate side channel
256 launch { // this is a very fast consumer for the side channel
257 side.consumeEach { println("Side channel has $it") }
258 }
259 produceNumbers(side).consumeEach {
260 println("Consuming $it")
261 delay(250) // let us digest the consumed number properly, do not hurry
262 }
263 println("Done consuming")
264 coroutineContext.cancelChildren()
265}
Alexander Prendotacbeef102018-09-27 18:42:04 +0300266```
267
268</div>
hadihariri7db55532018-09-15 10:35:08 +0200269
270> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-select-03.kt)
271
272So let us see what happens:
273
274```text
275Consuming 1
276Side channel has 2
277Side channel has 3
278Consuming 4
279Side channel has 5
280Side channel has 6
281Consuming 7
282Side channel has 8
283Side channel has 9
284Consuming 10
285Done consuming
286```
287
288<!--- TEST -->
289
290### Selecting deferred values
291
292Deferred values can be selected using [onAwait][Deferred.onAwait] clause.
293Let us start with an async function that returns a deferred string value after
294a random delay:
295
296<!--- INCLUDE .*/example-select-04.kt
297import java.util.*
298-->
299
Alexander Prendotacbeef102018-09-27 18:42:04 +0300300<div class="sample" markdown="1" theme="idea" data-highlight-only>
301
hadihariri7db55532018-09-15 10:35:08 +0200302```kotlin
303fun CoroutineScope.asyncString(time: Int) = async {
304 delay(time.toLong())
305 "Waited for $time ms"
306}
307```
308
Alexander Prendotacbeef102018-09-27 18:42:04 +0300309</div>
310
hadihariri7db55532018-09-15 10:35:08 +0200311Let us start a dozen of them with a random delay.
312
Alexander Prendotacbeef102018-09-27 18:42:04 +0300313<div class="sample" markdown="1" theme="idea" data-highlight-only>
314
hadihariri7db55532018-09-15 10:35:08 +0200315```kotlin
316fun CoroutineScope.asyncStringsList(): List<Deferred<String>> {
317 val random = Random(3)
318 return List(12) { asyncString(random.nextInt(1000)) }
319}
320```
321
Alexander Prendotacbeef102018-09-27 18:42:04 +0300322</div>
323
hadihariri7db55532018-09-15 10:35:08 +0200324Now the main function awaits for the first of them to complete and counts the number of deferred values
325that are still active. Note, that we've used here the fact that `select` expression is a Kotlin DSL,
326so we can provide clauses for it using an arbitrary code. In this case we iterate over a list
327of deferred values to provide `onAwait` clause for each deferred value.
328
Alexander Prendotacbeef102018-09-27 18:42:04 +0300329<div class="sample" markdown="1" theme="idea" data-highlight-only>
330
hadihariri7db55532018-09-15 10:35:08 +0200331```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300332fun main() = runBlocking<Unit> {
hadihariri7db55532018-09-15 10:35:08 +0200333 val list = asyncStringsList()
334 val result = select<String> {
335 list.withIndex().forEach { (index, deferred) ->
336 deferred.onAwait { answer ->
337 "Deferred $index produced answer '$answer'"
338 }
339 }
340 }
341 println(result)
342 val countActive = list.count { it.isActive }
343 println("$countActive coroutines are still active")
344}
345```
346
Alexander Prendotacbeef102018-09-27 18:42:04 +0300347</div>
348
hadihariri7db55532018-09-15 10:35:08 +0200349> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-select-04.kt)
350
351The output is:
352
353```text
354Deferred 4 produced answer 'Waited for 128 ms'
35511 coroutines are still active
356```
357
358<!--- TEST -->
359
360### Switch over a channel of deferred values
361
362Let us write a channel producer function that consumes a channel of deferred string values, waits for each received
363deferred value, but only until the next deferred value comes over or the channel is closed. This example puts together
364[onReceiveOrNull][ReceiveChannel.onReceiveOrNull] and [onAwait][Deferred.onAwait] clauses in the same `select`:
365
366<!--- INCLUDE
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300367import kotlin.coroutines.*
hadihariri7db55532018-09-15 10:35:08 +0200368-->
369
Alexander Prendotacbeef102018-09-27 18:42:04 +0300370<div class="sample" markdown="1" theme="idea" data-highlight-only>
371
hadihariri7db55532018-09-15 10:35:08 +0200372```kotlin
373fun CoroutineScope.switchMapDeferreds(input: ReceiveChannel<Deferred<String>>) = produce<String> {
374 var current = input.receive() // start with first received deferred value
375 while (isActive) { // loop while not cancelled/closed
376 val next = select<Deferred<String>?> { // return next deferred value from this select or null
377 input.onReceiveOrNull { update ->
378 update // replaces next value to wait
379 }
380 current.onAwait { value ->
381 send(value) // send value that current deferred has produced
382 input.receiveOrNull() // and use the next deferred from the input channel
383 }
384 }
385 if (next == null) {
386 println("Channel was closed")
387 break // out of loop
388 } else {
389 current = next
390 }
391 }
392}
393```
394
Alexander Prendotacbeef102018-09-27 18:42:04 +0300395</div>
396
hadihariri7db55532018-09-15 10:35:08 +0200397To test it, we'll use a simple async function that resolves to a specified string after a specified time:
398
Alexander Prendotacbeef102018-09-27 18:42:04 +0300399
400<div class="sample" markdown="1" theme="idea" data-highlight-only>
401
hadihariri7db55532018-09-15 10:35:08 +0200402```kotlin
403fun CoroutineScope.asyncString(str: String, time: Long) = async {
404 delay(time)
405 str
406}
407```
408
Alexander Prendotacbeef102018-09-27 18:42:04 +0300409</div>
410
hadihariri7db55532018-09-15 10:35:08 +0200411The main function just launches a coroutine to print results of `switchMapDeferreds` and sends some test
412data to it:
413
Alexander Prendotacbeef102018-09-27 18:42:04 +0300414<div class="sample" markdown="1" theme="idea" data-highlight-only>
415
hadihariri7db55532018-09-15 10:35:08 +0200416```kotlin
Prendota65e6c8c2018-10-17 11:51:08 +0300417fun main() = runBlocking<Unit> {
hadihariri7db55532018-09-15 10:35:08 +0200418 val chan = Channel<Deferred<String>>() // the channel for test
419 launch { // launch printing coroutine
420 for (s in switchMapDeferreds(chan))
421 println(s) // print each received string
422 }
423 chan.send(asyncString("BEGIN", 100))
424 delay(200) // enough time for "BEGIN" to be produced
425 chan.send(asyncString("Slow", 500))
426 delay(100) // not enough time to produce slow
427 chan.send(asyncString("Replace", 100))
428 delay(500) // give it time before the last one
429 chan.send(asyncString("END", 500))
430 delay(1000) // give it time to process
431 chan.close() // close the channel ...
432 delay(500) // and wait some time to let it finish
433}
434```
435
Alexander Prendotacbeef102018-09-27 18:42:04 +0300436</div>
437
hadihariri7db55532018-09-15 10:35:08 +0200438> You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-select-05.kt)
439
440The result of this code:
441
442```text
443BEGIN
444Replace
445END
446Channel was closed
447```
448
449<!--- TEST -->
450
451<!--- MODULE kotlinx-coroutines-core -->
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300452<!--- INDEX kotlinx.coroutines -->
453[Deferred.onAwait]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/on-await.html
454<!--- INDEX kotlinx.coroutines.channels -->
455[ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html
456[ReceiveChannel.onReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive.html
457[ReceiveChannel.onReceiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive-or-null.html
458[SendChannel.send]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/send.html
459[SendChannel.onSend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/on-send.html
460<!--- INDEX kotlinx.coroutines.selects -->
461[select]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.selects/select.html
hadihariri7db55532018-09-15 10:35:08 +0200462<!--- END -->