Skip to content

Commit d24ad36

Browse files
committed
Fixed reactive guide (better subject coverage, cancel logic change)
1 parent c40c781 commit d24ad36

File tree

5 files changed

+35
-32
lines changed

5 files changed

+35
-32
lines changed

reactive/coroutines-guide-reactive.md

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ You can subscribe to subjects from a coroutine just as with any other reactive s
419419

420420
<!--- INCLUDE
421421
import io.reactivex.subjects.BehaviorSubject
422+
import kotlinx.coroutines.experimental.Unconfined
422423
import kotlinx.coroutines.experimental.launch
423424
import kotlinx.coroutines.experimental.runBlocking
424425
import kotlinx.coroutines.experimental.rx2.consumeEach
@@ -430,7 +431,7 @@ fun main(args: Array<String>) = runBlocking<Unit> {
430431
subject.onNext("one")
431432
subject.onNext("two")
432433
// now launch a coroutine to print everything
433-
launch(context) { // use the context of the main thread for a coroutine
434+
launch(Unconfined) { // launch coroutine in unconfined context
434435
subject.consumeEach { println(it) }
435436
}
436437
subject.onNext("three")
@@ -440,20 +441,26 @@ fun main(args: Array<String>) = runBlocking<Unit> {
440441

441442
> You can get full code [here](kotlinx-coroutines-rx2/src/test/kotlin/guide/example-reactive-basic-07.kt)
442443
443-
The result is different, though:
444+
The result the same:
444445

445446
```text
447+
two
448+
three
446449
four
447450
```
448451

449452
<!--- TEST -->
450453

451-
It prints only the value value, because the coroutine is working in the main thread, which is busy updating the
452-
subject value. Only when the main thread completes, the subscribing coroutine has a change to print anything. By that
453-
time, the subject had already updated its value to "four".
454+
Here we use [Unconfined] coroutine context to launch consuming coroutine with the same behaviour as subscription in Rx.
455+
It basically means that the launched coroutine is going to be immediately executed in the same thread that
456+
is emitting elements. Contexts are covered in more details in a [separate section](#coroutine-context).
454457

455-
The coroutines in the main thread are scheduled cooperatively. There is a [yield] function to explicitly relinquish
456-
the control of the thread to other coroutines. We can add it to the last example:
458+
The advantage of coroutines is that it is easy to get conflation behavior for single-threaded UI updates.
459+
A typical UI application does not need to react to every state change. Only the most recent state is relevant.
460+
A sequence of back-to-back updates to the application state needs to get reflected in UI only once,
461+
as soon as the UI thread is free. For the following example we are going to simulate this by launching
462+
consuming coroutine in the context of the main thread and use [yield] function to simulate a break in the
463+
sequence of updates and to release the main thread:
457464

458465
<!--- INCLUDE
459466
import io.reactivex.subjects.BehaviorSubject
@@ -468,33 +475,29 @@ fun main(args: Array<String>) = runBlocking<Unit> {
468475
val subject = BehaviorSubject.create<String>()
469476
subject.onNext("one")
470477
subject.onNext("two")
471-
// now launch a coroutine to print everything
478+
// now launch a coroutine to print the most recent update
472479
launch(context) { // use the context of the main thread for a coroutine
473480
subject.consumeEach { println(it) }
474481
}
475482
subject.onNext("three")
476-
yield() // yield the main thread to the launched coroutine <--- HERE
477483
subject.onNext("four")
484+
yield() // yield the main thread to the launched coroutine <--- HERE
478485
}
479486
```
480487

481488
> You can get full code [here](kotlinx-coroutines-rx2/src/test/kotlin/guide/example-reactive-basic-08.kt)
482489
483-
Now coroutine has a chance to process (print) the "three" state of the subject, too:
490+
Now coroutine process (prints) only the most recent update:
484491

485492
```text
486-
three
487493
four
488494
```
489495

490496
<!--- TEST -->
491497

492-
This is quite the desired behavior for any kind of state-holding variable that needs to processed to update UI or
493-
other linked state, for example. There is no reason to react to back-to-back updates of the state.
494-
Only the most recent state is relevant.
495-
496-
The corresponding behavior in coroutines world is implemented by [ConflatedBroadcastChannel] that provides the same logic
497-
on top of coroutine channels directly, without going through the bridge to the reactive streams:
498+
The corresponding behavior in a pure coroutines world is implemented by [ConflatedBroadcastChannel]
499+
that provides the same logic on top of coroutine channels directly,
500+
without going through the bridge to the reactive streams:
498501

499502
<!--- INCLUDE
500503
import kotlinx.coroutines.experimental.channels.ConflatedBroadcastChannel
@@ -509,33 +512,32 @@ fun main(args: Array<String>) = runBlocking<Unit> {
509512
val broadcast = ConflatedBroadcastChannel<String>()
510513
broadcast.offer("one")
511514
broadcast.offer("two")
512-
// now launch a coroutine to print everything
515+
// now launch a coroutine to print the most recent update
513516
launch(context) { // use the context of the main thread for a coroutine
514517
broadcast.consumeEach { println(it) }
515518
}
516519
broadcast.offer("three")
517-
yield() // yield the main thread to the launched coroutine
518520
broadcast.offer("four")
521+
yield() // yield the main thread to the launched coroutine
519522
}
520523
```
521524

522525
> You can get full code [here](kotlinx-coroutines-rx2/src/test/kotlin/guide/example-reactive-basic-09.kt)
523526
524-
It produces the same output as the version based on `BehaviorSubject`:
527+
It produces the same output as the previous example based on `BehaviorSubject`:
525528

526529
```text
527-
three
528530
four
529531
```
530532

533+
<!--- TEST -->
534+
531535
Another implementation of [BroadcastChannel] is [ArrayBroadcastChannel]. It delivers every event to every
532536
subscriber since the moment the corresponding subscription is open. It corresponds to
533537
[PublishSubject][http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/subjects/PublishSubject.html] in Rx.
534538
The capacity of the buffer in the constructor of `ArrayBroadcastChannel` controls the numbers of elements
535539
that can be sent before the sender is suspended waiting for receiver to receive those elements.
536540

537-
<!--- TEST -->
538-
539541
## Operators
540542

541543
Full-featured reactive stream libraries, like Rx, come with
@@ -1046,11 +1048,11 @@ coroutines for complex pipelines with fan-in and fan-out between multiple worker
10461048
<!--- DOCS_ROOT kotlinx-coroutines-core/target/dokka/kotlinx-coroutines-core -->
10471049
<!--- INDEX kotlinx.coroutines.experimental -->
10481050
[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/run-blocking.html
1051+
[Unconfined]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-unconfined/index.html
10491052
[yield]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/yield.html
10501053
[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/launch.html
10511054
[CoroutineScope.context]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-coroutine-scope/context.html
10521055
[CommonPool]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-common-pool/index.html
1053-
[Unconfined]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-unconfined/index.html
10541056
[Job.join]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-job/join.html
10551057
<!--- INDEX kotlinx.coroutines.experimental.channels -->
10561058
[Channel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-channel/index.html

reactive/kotlinx-coroutines-rx2/src/test/kotlin/guide/example-reactive-basic-07.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package guide.reactive.basic.example07
1919

2020
import io.reactivex.subjects.BehaviorSubject
21+
import kotlinx.coroutines.experimental.Unconfined
2122
import kotlinx.coroutines.experimental.launch
2223
import kotlinx.coroutines.experimental.runBlocking
2324
import kotlinx.coroutines.experimental.rx2.consumeEach
@@ -27,9 +28,9 @@ fun main(args: Array<String>) = runBlocking<Unit> {
2728
subject.onNext("one")
2829
subject.onNext("two")
2930
// now launch a coroutine to print everything
30-
launch(context) { // use the context of the main thread for a coroutine
31+
launch(Unconfined) { // launch coroutine in unconfined context
3132
subject.consumeEach { println(it) }
3233
}
3334
subject.onNext("three")
3435
subject.onNext("four")
35-
}
36+
}

reactive/kotlinx-coroutines-rx2/src/test/kotlin/guide/example-reactive-basic-08.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ fun main(args: Array<String>) = runBlocking<Unit> {
2727
val subject = BehaviorSubject.create<String>()
2828
subject.onNext("one")
2929
subject.onNext("two")
30-
// now launch a coroutine to print everything
30+
// now launch a coroutine to print the most recent update
3131
launch(context) { // use the context of the main thread for a coroutine
3232
subject.consumeEach { println(it) }
3333
}
3434
subject.onNext("three")
35-
yield() // yield the main thread to the launched coroutine <--- HERE
3635
subject.onNext("four")
36+
yield() // yield the main thread to the launched coroutine <--- HERE
3737
}

reactive/kotlinx-coroutines-rx2/src/test/kotlin/guide/example-reactive-basic-09.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ fun main(args: Array<String>) = runBlocking<Unit> {
2727
val broadcast = ConflatedBroadcastChannel<String>()
2828
broadcast.offer("one")
2929
broadcast.offer("two")
30-
// now launch a coroutine to print everything
30+
// now launch a coroutine to print the most recent update
3131
launch(context) { // use the context of the main thread for a coroutine
3232
broadcast.consumeEach { println(it) }
3333
}
3434
broadcast.offer("three")
35-
yield() // yield the main thread to the launched coroutine
3635
broadcast.offer("four")
36+
yield() // yield the main thread to the launched coroutine
3737
}

reactive/kotlinx-coroutines-rx2/src/test/kotlin/guide/test/GuideReactiveTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,22 +82,22 @@ class GuideReactiveTest {
8282
@Test
8383
fun testGuideReactiveBasicExample07() {
8484
test { guide.reactive.basic.example07.main(emptyArray()) }.verifyLines(
85+
"two",
86+
"three",
8587
"four"
8688
)
8789
}
8890

8991
@Test
9092
fun testGuideReactiveBasicExample08() {
9193
test { guide.reactive.basic.example08.main(emptyArray()) }.verifyLines(
92-
"three",
9394
"four"
9495
)
9596
}
9697

9798
@Test
9899
fun testGuideReactiveBasicExample09() {
99100
test { guide.reactive.basic.example09.main(emptyArray()) }.verifyLines(
100-
"three",
101101
"four"
102102
)
103103
}

0 commit comments

Comments
 (0)