blob: 35480abf363c04fe3f7597f4b857eed9914613f6 [file] [log] [blame] [view]
hadihariri7db55532018-09-15 10:35:08 +02001<!--- INCLUDE .*/example-([a-z]+)-([0-9a-z]+)\.kt
2/*
Roman Elizarovdb0ef0c2019-07-03 15:02:44 +03003 * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
hadihariri7db55532018-09-15 10:35:08 +02004 */
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-->
Vsevolod Tolstopyatove50a0fa2019-01-28 11:34:24 +03009<!--- KNIT ../kotlinx-coroutines-core/jvm/test/guide/.*\.kt -->
10<!--- TEST_OUT ../kotlinx-coroutines-core/jvm/test/guide/test/SelectGuideTest.kt
hadihariri7db55532018-09-15 10:35:08 +020011// 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
Prendotab8a559d2018-11-30 16:24:23 +030020**Table of contents**
hadihariri7db55532018-09-15 10:35:08 +020021
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
Inego69c26df2019-04-21 14:51:25 +0700147> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-select-01.kt).
hadihariri7db55532018-09-15 10:35:08 +0200148
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
Vsevolod Tolstopyatova8904e22019-07-17 17:22:05 -0700166`select` to throw an exception. We can use [onReceiveOrNull][onReceiveOrNull] clause to perform a
hadihariri7db55532018-09-15 10:35:08 +0200167specific 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
Vsevolod Tolstopyatova8904e22019-07-17 17:22:05 -0700192Note that [onReceiveOrNull][onReceiveOrNull] is an extension function defined only
193for channels with non-nullable elements so that there is no accidental confusion between a closed channel
194and a null value.
195
hadihariri7db55532018-09-15 10:35:08 +0200196Let's use it with channel `a` that produces "Hello" string four times and
197channel `b` that produces "World" four times:
198
Prendota0eee3c32018-10-22 12:52:56 +0300199<!--- CLEAR -->
200
201<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300202
hadihariri7db55532018-09-15 10:35:08 +0200203```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300204import kotlinx.coroutines.*
205import kotlinx.coroutines.channels.*
206import kotlinx.coroutines.selects.*
207
208suspend fun selectAorB(a: ReceiveChannel<String>, b: ReceiveChannel<String>): String =
209 select<String> {
210 a.onReceiveOrNull { value ->
211 if (value == null)
212 "Channel 'a' is closed"
213 else
214 "a -> '$value'"
215 }
216 b.onReceiveOrNull { value ->
217 if (value == null)
218 "Channel 'b' is closed"
219 else
220 "b -> '$value'"
221 }
222 }
223
Prendota65e6c8c2018-10-17 11:51:08 +0300224fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +0300225//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200226 val a = produce<String> {
227 repeat(4) { send("Hello $it") }
228 }
229 val b = produce<String> {
230 repeat(4) { send("World $it") }
231 }
232 repeat(8) { // print first eight results
233 println(selectAorB(a, b))
234 }
Prendota0eee3c32018-10-22 12:52:56 +0300235 coroutineContext.cancelChildren()
236//sampleEnd
237}
hadihariri7db55532018-09-15 10:35:08 +0200238```
239
Alexander Prendotacbeef102018-09-27 18:42:04 +0300240</div>
241
Inego69c26df2019-04-21 14:51:25 +0700242> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt).
hadihariri7db55532018-09-15 10:35:08 +0200243
244The result of this code is quite interesting, so we'll analyze it in mode detail:
245
246```text
247a -> 'Hello 0'
248a -> 'Hello 1'
249b -> 'World 0'
250a -> 'Hello 2'
251a -> 'Hello 3'
252b -> 'World 1'
253Channel 'a' is closed
254Channel 'a' is closed
255```
256
257<!--- TEST -->
258
259There are couple of observations to make out of it.
260
261First of all, `select` is _biased_ to the first clause. When several clauses are selectable at the same time,
262the first one among them gets selected. Here, both channels are constantly producing strings, so `a` channel,
263being the first clause in select, wins. However, because we are using unbuffered channel, the `a` gets suspended from
264time to time on its [send][SendChannel.send] invocation and gives a chance for `b` to send, too.
265
Vsevolod Tolstopyatova8904e22019-07-17 17:22:05 -0700266The second observation, is that [onReceiveOrNull][onReceiveOrNull] gets immediately selected when the
hadihariri7db55532018-09-15 10:35:08 +0200267channel is already closed.
268
269### Selecting to send
270
271Select expression has [onSend][SendChannel.onSend] clause that can be used for a great good in combination
272with a biased nature of selection.
273
274Let us write an example of producer of integers that sends its values to a `side` channel when
275the consumers on its primary channel cannot keep up with it:
276
Alexander Prendotacbeef102018-09-27 18:42:04 +0300277<div class="sample" markdown="1" theme="idea" data-highlight-only>
278
hadihariri7db55532018-09-15 10:35:08 +0200279```kotlin
280fun CoroutineScope.produceNumbers(side: SendChannel<Int>) = produce<Int> {
281 for (num in 1..10) { // produce 10 numbers from 1 to 10
282 delay(100) // every 100 ms
283 select<Unit> {
284 onSend(num) {} // Send to the primary channel
285 side.onSend(num) {} // or to the side channel
286 }
287 }
288}
289```
290
Alexander Prendotacbeef102018-09-27 18:42:04 +0300291</div>
292
hadihariri7db55532018-09-15 10:35:08 +0200293Consumer is going to be quite slow, taking 250 ms to process each number:
294
Prendota0eee3c32018-10-22 12:52:56 +0300295<!--- CLEAR -->
296
297<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300298
hadihariri7db55532018-09-15 10:35:08 +0200299```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300300import kotlinx.coroutines.*
301import kotlinx.coroutines.channels.*
302import kotlinx.coroutines.selects.*
303
304fun CoroutineScope.produceNumbers(side: SendChannel<Int>) = produce<Int> {
305 for (num in 1..10) { // produce 10 numbers from 1 to 10
306 delay(100) // every 100 ms
307 select<Unit> {
308 onSend(num) {} // Send to the primary channel
309 side.onSend(num) {} // or to the side channel
310 }
311 }
312}
313
Prendota65e6c8c2018-10-17 11:51:08 +0300314fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +0300315//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200316 val side = Channel<Int>() // allocate side channel
317 launch { // this is a very fast consumer for the side channel
318 side.consumeEach { println("Side channel has $it") }
319 }
320 produceNumbers(side).consumeEach {
321 println("Consuming $it")
322 delay(250) // let us digest the consumed number properly, do not hurry
323 }
324 println("Done consuming")
Prendota0eee3c32018-10-22 12:52:56 +0300325 coroutineContext.cancelChildren()
326//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200327}
Alexander Prendotacbeef102018-09-27 18:42:04 +0300328```
329
330</div>
hadihariri7db55532018-09-15 10:35:08 +0200331
Inego69c26df2019-04-21 14:51:25 +0700332> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-select-03.kt).
hadihariri7db55532018-09-15 10:35:08 +0200333
334So let us see what happens:
335
336```text
337Consuming 1
338Side channel has 2
339Side channel has 3
340Consuming 4
341Side channel has 5
342Side channel has 6
343Consuming 7
344Side channel has 8
345Side channel has 9
346Consuming 10
347Done consuming
348```
349
350<!--- TEST -->
351
352### Selecting deferred values
353
354Deferred values can be selected using [onAwait][Deferred.onAwait] clause.
355Let us start with an async function that returns a deferred string value after
356a random delay:
357
Alexander Prendotacbeef102018-09-27 18:42:04 +0300358<div class="sample" markdown="1" theme="idea" data-highlight-only>
359
hadihariri7db55532018-09-15 10:35:08 +0200360```kotlin
361fun CoroutineScope.asyncString(time: Int) = async {
362 delay(time.toLong())
363 "Waited for $time ms"
364}
365```
366
Alexander Prendotacbeef102018-09-27 18:42:04 +0300367</div>
368
hadihariri7db55532018-09-15 10:35:08 +0200369Let us start a dozen of them with a random delay.
370
Alexander Prendotacbeef102018-09-27 18:42:04 +0300371<div class="sample" markdown="1" theme="idea" data-highlight-only>
372
hadihariri7db55532018-09-15 10:35:08 +0200373```kotlin
374fun CoroutineScope.asyncStringsList(): List<Deferred<String>> {
375 val random = Random(3)
376 return List(12) { asyncString(random.nextInt(1000)) }
377}
378```
379
Alexander Prendotacbeef102018-09-27 18:42:04 +0300380</div>
381
hadihariri7db55532018-09-15 10:35:08 +0200382Now the main function awaits for the first of them to complete and counts the number of deferred values
Inegoebe519a2019-04-21 13:22:27 +0700383that are still active. Note that we've used here the fact that `select` expression is a Kotlin DSL,
hadihariri7db55532018-09-15 10:35:08 +0200384so we can provide clauses for it using an arbitrary code. In this case we iterate over a list
385of deferred values to provide `onAwait` clause for each deferred value.
386
Prendota0eee3c32018-10-22 12:52:56 +0300387<!--- CLEAR -->
388
389<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300390
hadihariri7db55532018-09-15 10:35:08 +0200391```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300392import kotlinx.coroutines.*
393import kotlinx.coroutines.selects.*
394import java.util.*
395
396fun CoroutineScope.asyncString(time: Int) = async {
397 delay(time.toLong())
398 "Waited for $time ms"
399}
400
401fun CoroutineScope.asyncStringsList(): List<Deferred<String>> {
402 val random = Random(3)
403 return List(12) { asyncString(random.nextInt(1000)) }
404}
405
Prendota65e6c8c2018-10-17 11:51:08 +0300406fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +0300407//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200408 val list = asyncStringsList()
409 val result = select<String> {
410 list.withIndex().forEach { (index, deferred) ->
411 deferred.onAwait { answer ->
412 "Deferred $index produced answer '$answer'"
413 }
414 }
415 }
416 println(result)
417 val countActive = list.count { it.isActive }
418 println("$countActive coroutines are still active")
Prendota0eee3c32018-10-22 12:52:56 +0300419//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200420}
421```
422
Alexander Prendotacbeef102018-09-27 18:42:04 +0300423</div>
424
Inego69c26df2019-04-21 14:51:25 +0700425> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-select-04.kt).
hadihariri7db55532018-09-15 10:35:08 +0200426
427The output is:
428
429```text
430Deferred 4 produced answer 'Waited for 128 ms'
43111 coroutines are still active
432```
433
434<!--- TEST -->
435
436### Switch over a channel of deferred values
437
438Let us write a channel producer function that consumes a channel of deferred string values, waits for each received
439deferred value, but only until the next deferred value comes over or the channel is closed. This example puts together
Vsevolod Tolstopyatova8904e22019-07-17 17:22:05 -0700440[onReceiveOrNull][onReceiveOrNull] and [onAwait][Deferred.onAwait] clauses in the same `select`:
hadihariri7db55532018-09-15 10:35:08 +0200441
Alexander Prendotacbeef102018-09-27 18:42:04 +0300442<div class="sample" markdown="1" theme="idea" data-highlight-only>
443
hadihariri7db55532018-09-15 10:35:08 +0200444```kotlin
445fun CoroutineScope.switchMapDeferreds(input: ReceiveChannel<Deferred<String>>) = produce<String> {
446 var current = input.receive() // start with first received deferred value
447 while (isActive) { // loop while not cancelled/closed
448 val next = select<Deferred<String>?> { // return next deferred value from this select or null
449 input.onReceiveOrNull { update ->
450 update // replaces next value to wait
451 }
452 current.onAwait { value ->
453 send(value) // send value that current deferred has produced
454 input.receiveOrNull() // and use the next deferred from the input channel
455 }
456 }
457 if (next == null) {
458 println("Channel was closed")
459 break // out of loop
460 } else {
461 current = next
462 }
463 }
464}
465```
466
Alexander Prendotacbeef102018-09-27 18:42:04 +0300467</div>
468
hadihariri7db55532018-09-15 10:35:08 +0200469To test it, we'll use a simple async function that resolves to a specified string after a specified time:
470
Alexander Prendotacbeef102018-09-27 18:42:04 +0300471
472<div class="sample" markdown="1" theme="idea" data-highlight-only>
473
hadihariri7db55532018-09-15 10:35:08 +0200474```kotlin
475fun CoroutineScope.asyncString(str: String, time: Long) = async {
476 delay(time)
477 str
478}
479```
480
Alexander Prendotacbeef102018-09-27 18:42:04 +0300481</div>
482
hadihariri7db55532018-09-15 10:35:08 +0200483The main function just launches a coroutine to print results of `switchMapDeferreds` and sends some test
484data to it:
485
Prendota0eee3c32018-10-22 12:52:56 +0300486<!--- CLEAR -->
487
488<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3">
Alexander Prendotacbeef102018-09-27 18:42:04 +0300489
hadihariri7db55532018-09-15 10:35:08 +0200490```kotlin
Prendota0eee3c32018-10-22 12:52:56 +0300491import kotlinx.coroutines.*
492import kotlinx.coroutines.channels.*
493import kotlinx.coroutines.selects.*
494
495fun CoroutineScope.switchMapDeferreds(input: ReceiveChannel<Deferred<String>>) = produce<String> {
496 var current = input.receive() // start with first received deferred value
497 while (isActive) { // loop while not cancelled/closed
498 val next = select<Deferred<String>?> { // return next deferred value from this select or null
499 input.onReceiveOrNull { update ->
500 update // replaces next value to wait
501 }
502 current.onAwait { value ->
503 send(value) // send value that current deferred has produced
504 input.receiveOrNull() // and use the next deferred from the input channel
505 }
506 }
507 if (next == null) {
508 println("Channel was closed")
509 break // out of loop
510 } else {
511 current = next
512 }
513 }
514}
515
516fun CoroutineScope.asyncString(str: String, time: Long) = async {
517 delay(time)
518 str
519}
520
Prendota65e6c8c2018-10-17 11:51:08 +0300521fun main() = runBlocking<Unit> {
Prendota0eee3c32018-10-22 12:52:56 +0300522//sampleStart
hadihariri7db55532018-09-15 10:35:08 +0200523 val chan = Channel<Deferred<String>>() // the channel for test
524 launch { // launch printing coroutine
525 for (s in switchMapDeferreds(chan))
526 println(s) // print each received string
527 }
528 chan.send(asyncString("BEGIN", 100))
529 delay(200) // enough time for "BEGIN" to be produced
530 chan.send(asyncString("Slow", 500))
531 delay(100) // not enough time to produce slow
532 chan.send(asyncString("Replace", 100))
533 delay(500) // give it time before the last one
534 chan.send(asyncString("END", 500))
535 delay(1000) // give it time to process
536 chan.close() // close the channel ...
537 delay(500) // and wait some time to let it finish
Prendota0eee3c32018-10-22 12:52:56 +0300538//sampleEnd
hadihariri7db55532018-09-15 10:35:08 +0200539}
540```
541
Alexander Prendotacbeef102018-09-27 18:42:04 +0300542</div>
543
Inego69c26df2019-04-21 14:51:25 +0700544> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt).
hadihariri7db55532018-09-15 10:35:08 +0200545
546The result of this code:
547
548```text
549BEGIN
550Replace
551END
552Channel was closed
553```
554
555<!--- TEST -->
556
557<!--- MODULE kotlinx-coroutines-core -->
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300558<!--- INDEX kotlinx.coroutines -->
559[Deferred.onAwait]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/on-await.html
560<!--- INDEX kotlinx.coroutines.channels -->
561[ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html
562[ReceiveChannel.onReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive.html
Vsevolod Tolstopyatova8904e22019-07-17 17:22:05 -0700563[onReceiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/on-receive-or-null.html
Roman Elizarov0950dfa2018-07-13 10:33:25 +0300564[SendChannel.send]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/send.html
565[SendChannel.onSend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/on-send.html
566<!--- INDEX kotlinx.coroutines.selects -->
567[select]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.selects/select.html
hadihariri7db55532018-09-15 10:35:08 +0200568<!--- END -->