Skip to content

Commit bf6630f

Browse files
committed
Corrections to the Guide to UI programming with coroutines (mostly typos)
1 parent 4638d79 commit bf6630f

File tree

7 files changed

+58
-58
lines changed

7 files changed

+58
-58
lines changed

ui/coroutines-guide-ui.md

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ different UI application libraries:
8484
* [kotlinx-coroutines-swing](kotlinx-coroutines-swing) -- `Swing` context for Swing UI applications.
8585

8686
This guide covers all UI libraries simultaneously, because each of these modules consists of just one
87-
object definition that is couple of pages long. You can use any of them as an example to write the corresponding
87+
object definition that is a couple of pages long. You can use any of them as an example to write the corresponding
8888
context object for your favourite UI library, even if it is not included out of the box here.
8989

9090
## Table of contents
@@ -111,9 +111,9 @@ context object for your favourite UI library, even if it is not included out of
111111

112112
## Setup
113113

114-
The runnable examples in this guide are be presented for JavaFx. The advantage is that all the examples can
114+
The runnable examples in this guide are presented for JavaFx. The advantage is that all the examples can
115115
be directly started on any OS without the need for emulators or anything like that and they are fully self-contained
116-
(all code is in one file).
116+
(each example is in one file).
117117
There are separate notes on what changes need to be made (if any) to reproduce them on Android.
118118

119119
### JavaFx
@@ -145,17 +145,17 @@ experiment with them by making changes.
145145
Follow the guide on [Getting Started With Android and Kotlin](https://kotlinlang.org/docs/tutorials/kotlin-android.html)
146146
to create Kotlin project in Android Studio. You are also encouraged to add
147147
[Kotlin Android Extensions](https://kotlinlang.org/docs/tutorials/android-plugin.html)
148-
to you application.
148+
to your application.
149149

150150
In Android Studio 2.3 you'll get an application that looks similarly to the one shown below:
151151

152152
![UI example for Android](ui-example-android.png)
153153

154-
Go the `context_main.xml` of your application and assign an ID of "hello" to the text view with "Hello World!" string,
154+
Go to the `context_main.xml` of your application and assign an ID of "hello" to the text view with "Hello World!" string,
155155
so that it is available in your application as `hello` with Kotlin Android extensions. The pinkish floating
156156
action button is already named `fab` in the project template that gets created.
157157

158-
In the `MainActivity.kt` of you application remove the block `fab.setOnClickListener { ... }` and instead
158+
In the `MainActivity.kt` of your application remove the block `fab.setOnClickListener { ... }` and instead
159159
add `setup(hello, fab)` invocation as the last line of `onCreate` function.
160160
Create a placeholder `setup` function at the end of the file.
161161
That is where various code is placed in the rest of this guide:
@@ -203,7 +203,7 @@ import kotlinx.coroutines.experimental.javafx.JavaFx as UI
203203

204204
<!--- CLEAR -->
205205

206-
Coroutines confined to UI thread can freely update anything in UI and suspend without blocking the UI thread.
206+
Coroutines confined to the UI thread can freely update anything in UI and suspend without blocking the UI thread.
207207
For example, we can perform animations by coding them in imperative style. The following code updates the
208208
text with a 10 to 1 countdown twice a second, using [launch] coroutine builder:
209209

@@ -231,7 +231,7 @@ while `delay` waits, because it does not block the UI thread -- it just suspends
231231
### Cancel UI coroutine
232232

233233
We can keep a reference to the [Job] object that `launch` function returns and use it to cancel
234-
coroutine when want to stop it. Let us cancel the coroutine when pinkish circle is clicked:
234+
coroutine when we want to stop it. Let us cancel the coroutine when pinkish circle is clicked:
235235

236236
```kotlin
237237
fun setup(hello: Text, fab: Circle) {
@@ -286,13 +286,13 @@ fun setup(hello: Text, fab: Circle) {
286286
<!--- INCLUDE .*/example-ui-actor-([0-9]+).kt -->
287287

288288
Our first implementation for `onClick` just launches a new coroutine on each mouse event and
289-
passes the corresponding mouse event into the block (just in case we need it):
289+
passes the corresponding mouse event into the supplied action (just in case we need it):
290290

291291
```kotlin
292-
fun Node.onClick(block: suspend (MouseEvent) -> Unit) {
292+
fun Node.onClick(action: suspend (MouseEvent) -> Unit) {
293293
onMouseClicked = EventHandler { event ->
294294
launch(UI) {
295-
block(event)
295+
action(event)
296296
}
297297
}
298298
}
@@ -308,10 +308,10 @@ update the text. Try it. It does not look very good. We'll fix it later.
308308
on Android, so it is omitted.
309309

310310
```kotlin
311-
fun View.onClick(block: suspend () -> Unit) {
311+
fun View.onClick(action: suspend () -> Unit) {
312312
setOnClickListener {
313313
launch(UI) {
314-
block()
314+
action()
315315
}
316316
}
317317
}
@@ -323,15 +323,15 @@ fun View.onClick(block: suspend () -> Unit) {
323323

324324
We can cancel an active job before starting a new one to ensure that at most one coroutine is animating
325325
the countdown. However, it is generally not the best idea. The [cancel][Job.cancel] function serves only as a signal
326-
to abort coroutine. Cancellation is cooperative and coroutine may, at the moment, be doing something non-cancellable
326+
to abort a coroutine. Cancellation is cooperative and a coroutine may, at the moment, be doing something non-cancellable
327327
or otherwise ignore a cancellation signal. A better solution is to use an [actor] for tasks that should
328328
not be performed concurrently. Let us change `onClick` extension implementation:
329329

330330
```kotlin
331-
fun Node.onClick(block: suspend (MouseEvent) -> Unit) {
331+
fun Node.onClick(action: suspend (MouseEvent) -> Unit) {
332332
// launch one actor to handle all events on this node
333333
val eventActor = actor<MouseEvent>(UI) {
334-
for (event in channel) block(event) // pass event to block
334+
for (event in channel) action(event) // pass event to action
335335
}
336336
// install a listener to offer events to this actor
337337
onMouseClicked = EventHandler { event ->
@@ -344,7 +344,7 @@ fun Node.onClick(block: suspend (MouseEvent) -> Unit) {
344344
345345
The key idea that underlies an integration of an actor coroutine and a regular event handler is that
346346
there is an [offer][SendChannel.offer] function on [SendChannel] that does not wait. It sends an element to the actor immediately,
347-
if it is possible, or discards an element otherwise. An `offser` actually returns a `Boolean` result which we ignore here.
347+
if it is possible, or discards an element otherwise. An `offer` actually returns a `Boolean` result which we ignore here.
348348

349349
Try clicking repeatedly on a circle in this version of the code. The clicks are just ignored while the countdown
350350
animation is running. This happens because the actor is busy with an animation and does not receive from its channel.
@@ -355,10 +355,10 @@ the `receive` is active.
355355
The corresponding extension for `View` class looks like this:
356356

357357
```kotlin
358-
fun View.onClick(block: suspend () -> Unit) {
358+
fun View.onClick(action: suspend () -> Unit) {
359359
// launch one actor
360360
val eventActor = actor<Unit>(UI) {
361-
for (event in channel) block()
361+
for (event in channel) action()
362362
}
363363
// install a listener to activate this actor
364364
setOnClickListener {
@@ -377,14 +377,14 @@ processing the previous one. The [actor] coroutine builder accepts an optional
377377
controls the implementation of the channel that this actor is using for its mailbox. The description of all
378378
the available choices is given in documentation of the [Channel()][Channel.invoke] factory function.
379379

380-
Let us change to the code to use [ConflatedChannel] by passing [Channel.CONFLATED] capacity value. The
380+
Let us change the code to use [ConflatedChannel] by passing [Channel.CONFLATED] capacity value. The
381381
change is only to the line that creates an actor:
382382

383383
```kotlin
384-
fun Node.onClick(block: suspend (MouseEvent) -> Unit) {
384+
fun Node.onClick(action: suspend (MouseEvent) -> Unit) {
385385
// launch one actor to handle all events on this node
386386
val eventActor = actor<MouseEvent>(UI, capacity = Channel.CONFLATED) { // <--- Changed here
387-
for (event in channel) block(event) // pass event to block
387+
for (event in channel) action(event) // pass event to action
388388
}
389389
// install a listener to offer events to this actor
390390
onMouseClicked = EventHandler { event ->
@@ -410,21 +410,21 @@ events. In this case, the animation runs as many times and the circle is clicked
410410

411411
## Blocking operations
412412

413-
This section explains patterns on using UI coroutines with thread-blocking operations.
413+
This section explains how to use UI coroutines with thread-blocking operations.
414414

415415
### The problem of UI freezes
416416

417-
It would have been great if all APIs out there were written as suspending functions that never block an
417+
It would have been great if all APIs out there were written as suspending functions that never blocks an
418418
execution thread. However, it is quite often not the case. Sometimes you need to do a CPU-consuming computation
419419
or just need to invoke some 3rd party APIs for network access, for example, that blocks the invoker thread.
420-
You can cannot do that from the UI thread nor from them UI-confined coroutine directly, because that would
420+
You cannot do that from the UI thread nor from the UI-confined coroutine directly, because that would
421421
block the UI thread and cause the freeze up of the UI.
422422

423423
<!--- INCLUDE .*/example-ui-blocking-([0-9]+).kt
424424
425-
fun Node.onClick(block: suspend (MouseEvent) -> Unit) {
425+
fun Node.onClick(action: suspend (MouseEvent) -> Unit) {
426426
val eventActor = actor<MouseEvent>(UI, capacity = Channel.CONFLATED) {
427-
for (event in channel) block(event) // pass event to block
427+
for (event in channel) action(event) // pass event to action
428428
}
429429
onMouseClicked = EventHandler { event ->
430430
eventActor.offer(event)
@@ -457,7 +457,7 @@ fun setup(hello: Text, fab: Circle) {
457457
delay(100) // update the text every 100ms
458458
}
459459
}
460-
// compute next fibonacci number of each click
460+
// compute the next fibonacci number of each click
461461
var x = 1
462462
fab.onClick {
463463
result = "fib($x) = ${fib(x)}"
@@ -479,7 +479,7 @@ The fix for the blocking operations on the UI thread is quite straightforward wi
479479
convert our "blocking" `fib` function to a non-blocking suspending function that runs the computation in
480480
the background thread by using [run] function to change its execution context to [CommonPool] of background
481481
threads. Notice, that `fib` function is now marked with `suspend` modifier. It does not block the coroutine that
482-
it is invoked from anymore, but suspends its execution when the computation in background thread is working:
482+
it is invoked from anymore, but suspends its execution when the computation in the background thread is working:
483483

484484
<!--- INCLUDE .*/example-ui-blocking-0[23].kt
485485
@@ -514,8 +514,8 @@ You can run this code and verify that UI is not frozen while large Fibonacci num
514514
However, this code computes `fib` somewhat slower, because every recursive call to `fib` goes via `run`. This is
515515
not a big problem in practice, because `run` is smart enough to check that the coroutine is already running
516516
in the required context and avoids overhead of dispatching coroutine to a different thread again. It is an
517-
overhead nonetheless, which is visible on this primitive code that does nothing else of use, but only adds integers
518-
in between invocations to `run`. For some more substantial code, the overhead of an extra `run` invocation is
517+
overhead nonetheless, which is visible on this primitive code that does nothing else, but only adds integers
518+
in between invocations to `run`. For some more substantial code, the overhead of an extra `run` invocation is
519519
not going to be significant.
520520

521521
Still, this particular `fib` implementation can be made to run as fast as before, but in the background thread, by renaming
@@ -532,11 +532,11 @@ fun fibBlocking(x: Int): Int =
532532

533533
> You can get full code [here](kotlinx-coroutines-javafx/src/test/kotlin/guide/example-ui-blocking-03.kt).
534534
535-
You can now enjoy full-speed naive Fibonacci computation without blocking UI thread. All we need is `run(CommonPool)`.
535+
You can now enjoy full-speed naive Fibonacci computation without blocking the UI thread. All we need is `run(CommonPool)`.
536536

537-
Note, that because the `fib` function is invoked from a single actor in our code, there is at most one concurrent
537+
Note, that because the `fib` function is invoked from the single actor in our code, there is at most one concurrent
538538
computation of it at any given time, so this code has a natural limit on the resource utilization.
539-
It can saturate at most on CPU core at any given time.
539+
It can saturate at most one CPU core.
540540

541541
## Lifecycle
542542

@@ -546,15 +546,15 @@ This section outlines an approach to life-cycle management with coroutines.
546546

547547
A typical UI application has a number of elements with a lifecycle. Windows, UI controls, activities, views, fragments
548548
and other visual elements are created and destroyed. A long-running coroutine, performing some IO or a background
549-
computation, can retain reference to the corresponding UI elements for longer than it is needed, preventing garbage
549+
computation, can retain references to the corresponding UI elements for longer than it is needed, preventing garbage
550550
collection of the whole trees of UI objects that were already destroyed and will not be displayed anymore.
551551

552552
The natural solution to this problem is to associate a [Job] object with each UI object that has a lifecycle and create
553553
all the coroutines in the context of this job.
554554

555555
For example, in Android application an `Activity` is initially _created_ and is _destroyed_ when it is no longer
556556
needed and when its memory must be released. A natural solution is to attach an
557-
instance of `Job` to an instance of `Activity`. We can create a mini-framework for that,
557+
instance of a `Job` to an instance of an `Activity`. We can create a mini-framework for that,
558558
by defining the following `JobHolder` interface:
559559

560560
```kotlin
@@ -568,7 +568,7 @@ its `onDestroy` function to cancel the corresponding job:
568568

569569
```kotlin
570570
class MainActivity : AppCompatActivity(), JobHolder {
571-
override val job: Job = Job() // an instance of Job for this activity
571+
override val job: Job = Job() // the instance of a Job for this activity
572572

573573
override fun onDestroy() {
574574
super.onDestroy()
@@ -580,7 +580,7 @@ class MainActivity : AppCompatActivity(), JobHolder {
580580
```
581581

582582
We also need a convenient way to retrieve a job for any view in the application. This is straightforward, because
583-
an activity is a context of the views in it, so we can define the following `View.contextJob` extension property:
583+
an activity is an Android `Context` of the views in it, so we can define the following `View.contextJob` extension property:
584584

585585
```kotlin
586586
val View.contextJob: Job
@@ -590,17 +590,17 @@ val View.contextJob: Job
590590
Here we use [NonCancellable] implementation of the `Job` as a null-object for the case where our `contextJob`
591591
extension property is invoked in a context that does not have an attached job.
592592

593-
As convenience of having a `contextJob` available is that we can simply use it to start all the coroutines
593+
A convenience of having a `contextJob` available is that we can simply use it to start all the coroutines
594594
without having to worry about explicitly maintaining a list of the coroutines we had started.
595595
All the life-cycle management will be taken care of by the mechanics of parent-child relations between jobs.
596596

597597
For example, `View.onClick` extension from the previous section can now be defined using `contextJob`:
598598

599599
```kotlin
600-
fun View.onClick(block: suspend () -> Unit) {
601-
// launch one actor as a paren of the context job
600+
fun View.onClick(action: suspend () -> Unit) {
601+
// launch one actor as a parent of the context job
602602
val eventActor = actor<Unit>(contextJob + UI, capacity = Channel.CONFLATED) {
603-
for (event in channel) block()
603+
for (event in channel) action()
604604
}
605605
// install a listener to activate this actor
606606
setOnClickListener {
@@ -609,10 +609,10 @@ fun View.onClick(block: suspend () -> Unit) {
609609
}
610610
```
611611

612-
Notice `contextJob + UI` expression that is used to start an actor in the above code. It defines a coroutine context
613-
for our new actor that includes the job and UI dispatcher. The coroutine that is started by this
612+
Notice how `contextJob + UI` expression is used to start an actor in the above code. It defines a coroutine context
613+
for our new actor that includes the job and the `UI` dispatcher. The coroutine that is started by this
614614
`actor(contextJob + UI)` expression is going to become a child of the job of the corresponding context. When the
615-
activity is destroyed and its job is cancelled, all its children coroutines are cancelled.
615+
activity is destroyed and its job is cancelled all its children coroutines are cancelled, too.
616616

617617
Parent-child relation between jobs forms a hierarchy. A coroutine that performs some background job on behalf of
618618
the view and in its context can create further children coroutines. The whole tree of coroutines gets cancelled

ui/kotlinx-coroutines-javafx/src/test/kotlin/guide/example-ui-actor-01.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ fun setup(hello: Text, fab: Circle) {
7272
}
7373
}
7474

75-
fun Node.onClick(block: suspend (MouseEvent) -> Unit) {
75+
fun Node.onClick(action: suspend (MouseEvent) -> Unit) {
7676
onMouseClicked = EventHandler { event ->
7777
launch(UI) {
78-
block(event)
78+
action(event)
7979
}
8080
}
8181
}

ui/kotlinx-coroutines-javafx/src/test/kotlin/guide/example-ui-actor-02.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ fun setup(hello: Text, fab: Circle) {
7272
}
7373
}
7474

75-
fun Node.onClick(block: suspend (MouseEvent) -> Unit) {
75+
fun Node.onClick(action: suspend (MouseEvent) -> Unit) {
7676
// launch one actor to handle all events on this node
7777
val eventActor = actor<MouseEvent>(UI) {
78-
for (event in channel) block(event) // pass event to block
78+
for (event in channel) action(event) // pass event to action
7979
}
8080
// install a listener to offer events to this actor
8181
onMouseClicked = EventHandler { event ->

ui/kotlinx-coroutines-javafx/src/test/kotlin/guide/example-ui-actor-03.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ fun setup(hello: Text, fab: Circle) {
7272
}
7373
}
7474

75-
fun Node.onClick(block: suspend (MouseEvent) -> Unit) {
75+
fun Node.onClick(action: suspend (MouseEvent) -> Unit) {
7676
// launch one actor to handle all events on this node
7777
val eventActor = actor<MouseEvent>(UI, capacity = Channel.CONFLATED) { // <--- Changed here
78-
for (event in channel) block(event) // pass event to block
78+
for (event in channel) action(event) // pass event to action
7979
}
8080
// install a listener to offer events to this actor
8181
onMouseClicked = EventHandler { event ->

ui/kotlinx-coroutines-javafx/src/test/kotlin/guide/example-ui-blocking-01.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ class ExampleApp : Application() {
6262
}
6363
}
6464

65-
fun Node.onClick(block: suspend (MouseEvent) -> Unit) {
65+
fun Node.onClick(action: suspend (MouseEvent) -> Unit) {
6666
val eventActor = actor<MouseEvent>(UI, capacity = Channel.CONFLATED) {
67-
for (event in channel) block(event) // pass event to block
67+
for (event in channel) action(event) // pass event to action
6868
}
6969
onMouseClicked = EventHandler { event ->
7070
eventActor.offer(event)
@@ -84,7 +84,7 @@ fun setup(hello: Text, fab: Circle) {
8484
delay(100) // update the text every 100ms
8585
}
8686
}
87-
// compute next fibonacci number of each click
87+
// compute the next fibonacci number of each click
8888
var x = 1
8989
fab.onClick {
9090
result = "fib($x) = ${fib(x)}"

ui/kotlinx-coroutines-javafx/src/test/kotlin/guide/example-ui-blocking-02.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ class ExampleApp : Application() {
6262
}
6363
}
6464

65-
fun Node.onClick(block: suspend (MouseEvent) -> Unit) {
65+
fun Node.onClick(action: suspend (MouseEvent) -> Unit) {
6666
val eventActor = actor<MouseEvent>(UI, capacity = Channel.CONFLATED) {
67-
for (event in channel) block(event) // pass event to block
67+
for (event in channel) action(event) // pass event to action
6868
}
6969
onMouseClicked = EventHandler { event ->
7070
eventActor.offer(event)

ui/kotlinx-coroutines-javafx/src/test/kotlin/guide/example-ui-blocking-03.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ class ExampleApp : Application() {
6262
}
6363
}
6464

65-
fun Node.onClick(block: suspend (MouseEvent) -> Unit) {
65+
fun Node.onClick(action: suspend (MouseEvent) -> Unit) {
6666
val eventActor = actor<MouseEvent>(UI, capacity = Channel.CONFLATED) {
67-
for (event in channel) block(event) // pass event to block
67+
for (event in channel) action(event) // pass event to action
6868
}
6969
onMouseClicked = EventHandler { event ->
7070
eventActor.offer(event)

0 commit comments

Comments
 (0)