Skip to content

Commit 98532c9

Browse files
authored
Deprecation and migration of receiveOrNull and onReceiveOrNull. (#2612)
* Deprecation and migration of receiveOrNull and onReceiveOrNull. * Raise deprecation level for members, introduce deprecation for extensions * Explain rationale behind deprecation * Provide default implementation for deprecated members in Channel interface * Get rid of the internal implementation, leverage receiveCatching * Introduce new extensions for ChannelResult and use them as a replacement in our own operators Fixes #1676
1 parent 7ae273c commit 98532c9

38 files changed

+339
-372
lines changed

docs/topics/select-expression.md

+37-32
Original file line numberDiff line numberDiff line change
@@ -120,31 +120,32 @@ buzz -> 'Buzz!'
120120
## Selecting on close
121121

122122
The [onReceive][ReceiveChannel.onReceive] clause in `select` fails when the channel is closed causing the corresponding
123-
`select` to throw an exception. We can use [onReceiveOrNull][onReceiveOrNull] clause to perform a
123+
`select` to throw an exception. We can use [onReceiveCatching][ReceiveChannel.onReceiveCatching] clause to perform a
124124
specific action when the channel is closed. The following example also shows that `select` is an expression that returns
125125
the result of its selected clause:
126126

127127
```kotlin
128128
suspend fun selectAorB(a: ReceiveChannel<String>, b: ReceiveChannel<String>): String =
129129
select<String> {
130-
a.onReceiveOrNull { value ->
131-
if (value == null)
132-
"Channel 'a' is closed"
133-
else
130+
a.onReceiveCatching { it ->
131+
val value = it.getOrNull()
132+
if (value != null) {
134133
"a -> '$value'"
134+
} else {
135+
"Channel 'a' is closed"
136+
}
135137
}
136-
b.onReceiveOrNull { value ->
137-
if (value == null)
138-
"Channel 'b' is closed"
139-
else
138+
b.onReceiveCatching { it ->
139+
val value = it.getOrNull()
140+
if (value != null) {
140141
"b -> '$value'"
142+
} else {
143+
"Channel 'b' is closed"
144+
}
141145
}
142146
}
143147
```
144148

145-
Note that [onReceiveOrNull][onReceiveOrNull] is an extension function defined only
146-
for channels with non-nullable elements so that there is no accidental confusion between a closed channel
147-
and a null value.
148149

149150
Let's use it with channel `a` that produces "Hello" string four times and
150151
channel `b` that produces "World" four times:
@@ -158,17 +159,21 @@ import kotlinx.coroutines.selects.*
158159

159160
suspend fun selectAorB(a: ReceiveChannel<String>, b: ReceiveChannel<String>): String =
160161
select<String> {
161-
a.onReceiveOrNull { value ->
162-
if (value == null)
163-
"Channel 'a' is closed"
164-
else
162+
a.onReceiveCatching { it ->
163+
val value = it.getOrNull()
164+
if (value != null) {
165165
"a -> '$value'"
166+
} else {
167+
"Channel 'a' is closed"
168+
}
166169
}
167-
b.onReceiveOrNull { value ->
168-
if (value == null)
169-
"Channel 'b' is closed"
170-
else
170+
b.onReceiveCatching { it ->
171+
val value = it.getOrNull()
172+
if (value != null) {
171173
"b -> '$value'"
174+
} else {
175+
"Channel 'b' is closed"
176+
}
172177
}
173178
}
174179

@@ -215,7 +220,7 @@ the first one among them gets selected. Here, both channels are constantly produ
215220
being the first clause in select, wins. However, because we are using unbuffered channel, the `a` gets suspended from
216221
time to time on its [send][SendChannel.send] invocation and gives a chance for `b` to send, too.
217222

218-
The second observation, is that [onReceiveOrNull][onReceiveOrNull] gets immediately selected when the
223+
The second observation, is that [onReceiveCatching][ReceiveChannel.onReceiveCatching] gets immediately selected when the
219224
channel is already closed.
220225

221226
## Selecting to send
@@ -375,19 +380,19 @@ Deferred 4 produced answer 'Waited for 128 ms'
375380

376381
Let us write a channel producer function that consumes a channel of deferred string values, waits for each received
377382
deferred value, but only until the next deferred value comes over or the channel is closed. This example puts together
378-
[onReceiveOrNull][onReceiveOrNull] and [onAwait][Deferred.onAwait] clauses in the same `select`:
383+
[onReceiveCatching][ReceiveChannel.onReceiveCatching] and [onAwait][Deferred.onAwait] clauses in the same `select`:
379384

380385
```kotlin
381386
fun CoroutineScope.switchMapDeferreds(input: ReceiveChannel<Deferred<String>>) = produce<String> {
382387
var current = input.receive() // start with first received deferred value
383388
while (isActive) { // loop while not cancelled/closed
384389
val next = select<Deferred<String>?> { // return next deferred value from this select or null
385-
input.onReceiveOrNull { update ->
386-
update // replaces next value to wait
390+
input.onReceiveCatching { update ->
391+
update.getOrNull()
387392
}
388-
current.onAwait { value ->
393+
current.onAwait { value ->
389394
send(value) // send value that current deferred has produced
390-
input.receiveOrNull() // and use the next deferred from the input channel
395+
input.receiveCatching().getOrNull() // and use the next deferred from the input channel
391396
}
392397
}
393398
if (next == null) {
@@ -423,12 +428,12 @@ fun CoroutineScope.switchMapDeferreds(input: ReceiveChannel<Deferred<String>>) =
423428
var current = input.receive() // start with first received deferred value
424429
while (isActive) { // loop while not cancelled/closed
425430
val next = select<Deferred<String>?> { // return next deferred value from this select or null
426-
input.onReceiveOrNull { update ->
427-
update // replaces next value to wait
431+
input.onReceiveCatching { update ->
432+
update.getOrNull()
428433
}
429-
current.onAwait { value ->
434+
current.onAwait { value ->
430435
send(value) // send value that current deferred has produced
431-
input.receiveOrNull() // and use the next deferred from the input channel
436+
input.receiveCatching().getOrNull() // and use the next deferred from the input channel
432437
}
433438
}
434439
if (next == null) {
@@ -491,12 +496,12 @@ Channel was closed
491496

492497
[ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html
493498
[ReceiveChannel.onReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive.html
494-
[onReceiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/on-receive-or-null.html
499+
[ReceiveChannel.onReceiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive-catching.html
495500
[SendChannel.send]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/send.html
496501
[SendChannel.onSend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/on-send.html
497502

498503
<!--- INDEX kotlinx.coroutines.selects -->
499504

500505
[select]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.selects/select.html
501506

502-
<!--- END -->
507+
<!--- END -->

integration/kotlinx-coroutines-jdk8/test/time/FlowSampleTest.kt

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
package kotlinx.coroutines.time
@@ -12,8 +12,7 @@ import org.junit.Test
1212
import java.time.Duration
1313
import kotlin.test.assertEquals
1414

15-
16-
class SampleTest : TestBase() {
15+
class FlowSampleTest : TestBase() {
1716
@Test
1817
public fun testBasic() = withVirtualTime {
1918
expect(1)

kotlinx-coroutines-core/README.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ helper function. [NonCancellable] job object is provided to suppress cancellatio
5454
| ---------------- | --------------------------------------------- | ------------------------------------------------ | --------------------------
5555
| [Job] | [join][Job.join] | [onJoin][Job.onJoin] | [isCompleted][Job.isCompleted]
5656
| [Deferred] | [await][Deferred.await] | [onAwait][Deferred.onAwait] | [isCompleted][Job.isCompleted]
57-
| [SendChannel][kotlinx.coroutines.channels.SendChannel] | [send][kotlinx.coroutines.channels.SendChannel.send] | [onSend][kotlinx.coroutines.channels.SendChannel.onSend] | [offer][kotlinx.coroutines.channels.SendChannel.offer]
58-
| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | [onReceive][kotlinx.coroutines.channels.ReceiveChannel.onReceive] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll]
59-
| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveOrNull][kotlinx.coroutines.channels.receiveOrNull] | [onReceiveOrNull][kotlinx.coroutines.channels.onReceiveOrNull] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll]
57+
| [SendChannel][kotlinx.coroutines.channels.SendChannel] | [send][kotlinx.coroutines.channels.SendChannel.send] | [onSend][kotlinx.coroutines.channels.SendChannel.onSend] | [trySend][kotlinx.coroutines.channels.SendChannel.trySend]
58+
| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | [onReceive][kotlinx.coroutines.channels.ReceiveChannel.onReceive] | [tryReceive][kotlinx.coroutines.channels.ReceiveChannel.tryReceive]
59+
| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveCatching][kotlinx.coroutines.channels.ReceiveChannel.receiveCatching] | [onReceiveCatching][kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching] | [tryReceive][kotlinx.coroutines.channels.ReceiveChannel.tryReceive]
6060
| [Mutex][kotlinx.coroutines.sync.Mutex] | [lock][kotlinx.coroutines.sync.Mutex.lock] | [onLock][kotlinx.coroutines.sync.Mutex.onLock] | [tryLock][kotlinx.coroutines.sync.Mutex.tryLock]
6161
| none | [delay] | [onTimeout][kotlinx.coroutines.selects.SelectBuilder.onTimeout] | none
6262

@@ -133,11 +133,11 @@ Obsolete and deprecated module to test coroutines. Replaced with `kotlinx-corout
133133
[kotlinx.coroutines.channels.ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html
134134
[kotlinx.coroutines.channels.SendChannel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/index.html
135135
[kotlinx.coroutines.channels.SendChannel.onSend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/on-send.html
136-
[kotlinx.coroutines.channels.SendChannel.offer]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/offer.html
136+
[kotlinx.coroutines.channels.SendChannel.trySend]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/try-send.html
137137
[kotlinx.coroutines.channels.ReceiveChannel.onReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive.html
138-
[kotlinx.coroutines.channels.ReceiveChannel.poll]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/poll.html
139-
[kotlinx.coroutines.channels.receiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/receive-or-null.html
140-
[kotlinx.coroutines.channels.onReceiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/on-receive-or-null.html
138+
[kotlinx.coroutines.channels.ReceiveChannel.tryReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/try-receive.html
139+
[kotlinx.coroutines.channels.ReceiveChannel.receiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive-catching.html
140+
[kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive-catching.html
141141

142142
<!--- INDEX kotlinx.coroutines.selects -->
143143

kotlinx-coroutines-core/api/kotlinx-coroutines-core.api

+9
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,9 @@ public abstract interface class kotlinx/coroutines/channels/ActorScope : kotlinx
555555

556556
public final class kotlinx/coroutines/channels/ActorScope$DefaultImpls {
557557
public static synthetic fun cancel (Lkotlinx/coroutines/channels/ActorScope;)V
558+
public static fun getOnReceiveOrNull (Lkotlinx/coroutines/channels/ActorScope;)Lkotlinx/coroutines/selects/SelectClause1;
558559
public static fun poll (Lkotlinx/coroutines/channels/ActorScope;)Ljava/lang/Object;
560+
public static fun receiveOrNull (Lkotlinx/coroutines/channels/ActorScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
559561
}
560562

561563
public abstract interface class kotlinx/coroutines/channels/BroadcastChannel : kotlinx/coroutines/channels/SendChannel {
@@ -600,8 +602,10 @@ public abstract interface class kotlinx/coroutines/channels/Channel : kotlinx/co
600602

601603
public final class kotlinx/coroutines/channels/Channel$DefaultImpls {
602604
public static synthetic fun cancel (Lkotlinx/coroutines/channels/Channel;)V
605+
public static fun getOnReceiveOrNull (Lkotlinx/coroutines/channels/Channel;)Lkotlinx/coroutines/selects/SelectClause1;
603606
public static fun offer (Lkotlinx/coroutines/channels/Channel;Ljava/lang/Object;)Z
604607
public static fun poll (Lkotlinx/coroutines/channels/Channel;)Ljava/lang/Object;
608+
public static fun receiveOrNull (Lkotlinx/coroutines/channels/Channel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
605609
}
606610

607611
public final class kotlinx/coroutines/channels/Channel$Factory {
@@ -627,6 +631,9 @@ public final class kotlinx/coroutines/channels/ChannelKt {
627631
public static final fun Channel (ILkotlinx/coroutines/channels/BufferOverflow;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/channels/Channel;
628632
public static synthetic fun Channel$default (IILjava/lang/Object;)Lkotlinx/coroutines/channels/Channel;
629633
public static synthetic fun Channel$default (ILkotlinx/coroutines/channels/BufferOverflow;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/coroutines/channels/Channel;
634+
public static final fun getOrElse-WpGqRn0 (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
635+
public static final fun onFailure-WpGqRn0 (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
636+
public static final fun onSuccess-WpGqRn0 (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
630637
}
631638

632639
public final class kotlinx/coroutines/channels/ChannelResult {
@@ -840,7 +847,9 @@ public final class kotlinx/coroutines/channels/ReceiveChannel$DefaultImpls {
840847
public static synthetic fun cancel (Lkotlinx/coroutines/channels/ReceiveChannel;)V
841848
public static synthetic fun cancel$default (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/lang/Throwable;ILjava/lang/Object;)Z
842849
public static synthetic fun cancel$default (Lkotlinx/coroutines/channels/ReceiveChannel;Ljava/util/concurrent/CancellationException;ILjava/lang/Object;)V
850+
public static fun getOnReceiveOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;)Lkotlinx/coroutines/selects/SelectClause1;
843851
public static fun poll (Lkotlinx/coroutines/channels/ReceiveChannel;)Ljava/lang/Object;
852+
public static fun receiveOrNull (Lkotlinx/coroutines/channels/ReceiveChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
844853
}
845854

846855
public abstract interface class kotlinx/coroutines/channels/SendChannel {

kotlinx-coroutines-core/common/README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ helper function. [NonCancellable] job object is provided to suppress cancellatio
5959
| [Deferred] | [await][Deferred.await] | [onAwait][Deferred.onAwait] | [isCompleted][Job.isCompleted]
6060
| [SendChannel][kotlinx.coroutines.channels.SendChannel] | [send][kotlinx.coroutines.channels.SendChannel.send] | [onSend][kotlinx.coroutines.channels.SendChannel.onSend] | [offer][kotlinx.coroutines.channels.SendChannel.offer]
6161
| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receive][kotlinx.coroutines.channels.ReceiveChannel.receive] | [onReceive][kotlinx.coroutines.channels.ReceiveChannel.onReceive] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll]
62-
| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveOrNull][kotlinx.coroutines.channels.receiveOrNull] | [onReceiveOrNull][kotlinx.coroutines.channels.onReceiveOrNull] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll]
62+
| [ReceiveChannel][kotlinx.coroutines.channels.ReceiveChannel] | [receiveCatching][kotlinx.coroutines.channels.ReceiveChannel.receiveCatching] | [onReceiveCatching][kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching] | [poll][kotlinx.coroutines.channels.ReceiveChannel.poll]
6363
| [Mutex][kotlinx.coroutines.sync.Mutex] | [lock][kotlinx.coroutines.sync.Mutex.lock] | [onLock][kotlinx.coroutines.sync.Mutex.onLock] | [tryLock][kotlinx.coroutines.sync.Mutex.tryLock]
6464
| none | [delay] | [onTimeout][kotlinx.coroutines.selects.SelectBuilder.onTimeout] | none
6565

@@ -149,8 +149,8 @@ Low-level primitives for finer-grained control of coroutines.
149149
[kotlinx.coroutines.channels.SendChannel.offer]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/offer.html
150150
[kotlinx.coroutines.channels.ReceiveChannel.onReceive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive.html
151151
[kotlinx.coroutines.channels.ReceiveChannel.poll]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/poll.html
152-
[kotlinx.coroutines.channels.receiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/receive-or-null.html
153-
[kotlinx.coroutines.channels.onReceiveOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/on-receive-or-null.html
152+
[kotlinx.coroutines.channels.ReceiveChannel.receiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive-catching.html
153+
[kotlinx.coroutines.channels.ReceiveChannel.onReceiveCatching]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/on-receive-catching.html
154154

155155
<!--- INDEX kotlinx.coroutines.selects -->
156156

0 commit comments

Comments
 (0)