1# Guide to UI programming with coroutines
2
3This guide assumes familiarity with basic coroutine concepts that are
4covered in [Guide to kotlinx.coroutines](../docs/coroutines-guide.md) and gives specific
5examples on how to use coroutines in UI applications.
6
7All UI application libraries have one thing in common. They have the single main thread where all state of the UI
8is confined, and all updates to the UI has to happen in this particular thread. With respect to coroutines,
9it means that you need an appropriate _coroutine dispatcher context_ that confines the coroutine
10execution to this main UI thread.
11
12In particular, `kotlinx.coroutines` has three modules that provide coroutine context for
13different UI application libraries:
14
15* [kotlinx-coroutines-android](kotlinx-coroutines-android) -- `Dispatchers.Main` context for Android applications.
16* [kotlinx-coroutines-javafx](kotlinx-coroutines-javafx) -- `Dispatchers.JavaFx` context for JavaFX UI applications.
17* [kotlinx-coroutines-swing](kotlinx-coroutines-swing) -- `Dispatchers.Swing` context for Swing UI applications.
18
19Also, UI dispatcher is available via `Dispatchers.Main` from `kotlinx-coroutines-core` and corresponding
20implementation (Android, JavaFx or Swing) is discovered by [`ServiceLoader`](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) API.
21For example, if you are writing JavaFx application, you can use either `Dispatchers.Main` or `Dispachers.JavaFx` extension, it will be the same object.
22
23This guide covers all UI libraries simultaneously, because each of these modules consists of just one
24object definition that is a couple of pages long. You can use any of them as an example to write the corresponding
25context object for your favourite UI library, even if it is not included out of the box here.
26
27## Table of contents
28
29<!--- TOC -->
30
31* [Setup](#setup)
32  * [JavaFx](#javafx)
33  * [Android](#android)
34* [Basic UI coroutines](#basic-ui-coroutines)
35  * [Launch UI coroutine](#launch-ui-coroutine)
36  * [Cancel UI coroutine](#cancel-ui-coroutine)
37* [Using actors within UI context](#using-actors-within-ui-context)
38  * [Extensions for coroutines](#extensions-for-coroutines)
39  * [At most one concurrent job](#at-most-one-concurrent-job)
40  * [Event conflation](#event-conflation)
41* [Blocking operations](#blocking-operations)
42  * [The problem of UI freezes](#the-problem-of-ui-freezes)
43  * [Structured concurrency, lifecycle and coroutine parent-child hierarchy](#structured-concurrency-lifecycle-and-coroutine-parent-child-hierarchy)
44  * [Blocking operations](#blocking-operations)
45* [Advanced topics](#advanced-topics)
46  * [Starting coroutine in UI event handlers without dispatch](#starting-coroutine-in-ui-event-handlers-without-dispatch)
47
48<!--- END -->
49
50## Setup
51
52The runnable examples in this guide are presented for JavaFx. The advantage is that all the examples can
53be directly started on any OS without the need for emulators or anything like that and they are fully self-contained
54(each example is in one file).
55There are separate notes on what changes need to be made (if any) to reproduce them on Android.
56
57### JavaFx
58
59The basic example application for JavaFx consists of a window with a text label named `hello` that initially
60contains "Hello World!" string and a pinkish circle in the bottom-right corner named `fab` (floating action button).
61
62![UI example for JavaFx](ui-example-javafx.png)
63
64The `start` function of JavaFX application invokes `setup` function, passing it reference to `hello` and `fab`
65nodes. That is where various code is placed in the rest of this guide:
66
67```kotlin
68fun setup(hello: Text, fab: Circle) {
69    // placeholder
70}
71```
72
73> You can get the full code [here](kotlinx-coroutines-javafx/test/guide/example-ui-basic-01.kt).
74
75You can clone [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) project from GitHub onto your
76workstation and open the project in IDE. All the examples from this guide are in the test folder of
77[`ui/kotlinx-coroutines-javafx`](kotlinx-coroutines-javafx) module.
78This way you'll be able to run and see how each example works and to
79experiment with them by making changes.
80
81### Android
82
83Follow the guide on [Getting Started With Android and Kotlin](https://kotlinlang.org/docs/tutorials/kotlin-android.html)
84to create Kotlin project in Android Studio. You are also encouraged to add
85[Kotlin Android Extensions](https://kotlinlang.org/docs/tutorials/android-plugin.html)
86to your application.
87
88In Android Studio 2.3 you'll get an application that looks similarly to the one shown below:
89
90![UI example for Android](ui-example-android.png)
91
92Go to the `context_main.xml` of your application and assign an ID of "hello" to the text view with "Hello World!" string,
93so that it is available in your application as `hello` with Kotlin Android extensions. The pinkish floating
94action button is already named `fab` in the project template that gets created.
95
96In the `MainActivity.kt` of your application remove the block `fab.setOnClickListener { ... }` and instead
97add `setup(hello, fab)` invocation as the last line of `onCreate` function.
98Create a placeholder `setup` function at the end of the file.
99That is where various code is placed in the rest of this guide:
100
101```kotlin
102fun setup(hello: TextView, fab: FloatingActionButton) {
103    // placeholder
104}
105```
106
107<!--- CLEAR -->
108
109Add dependencies on `kotlinx-coroutines-android` module to the `dependencies { ... }` section of
110`app/build.gradle` file:
111
112```groovy
113implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1"
114```
115
116You can clone [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) project from GitHub onto your
117workstation. The resulting template project for Android resides in
118[`ui/kotlinx-coroutines-android/example-app`](kotlinx-coroutines-android/example-app) directory.
119You can load it in Android Studio to follow this guide on Android.
120
121## Basic UI coroutines
122
123This section shows basic usage of coroutines in UI applications.
124
125### Launch UI coroutine
126
127The `kotlinx-coroutines-javafx` module contains
128[Dispatchers.JavaFx][kotlinx.coroutines.Dispatchers.JavaFx]
129dispatcher that dispatches coroutine execution to
130the JavaFx application thread. We import it as `Main` to make all the presented examples
131easily portable to Android:
132
133```kotlin
134import kotlinx.coroutines.javafx.JavaFx as Main
135```
136
137<!--- CLEAR -->
138
139Coroutines confined to the main UI thread can freely update anything in UI and suspend without blocking the main thread.
140For example, we can perform animations by coding them in imperative style. The following code updates the
141text with a 10 to 1 countdown twice a second, using [launch] coroutine builder:
142
143```kotlin
144fun setup(hello: Text, fab: Circle) {
145    GlobalScope.launch(Dispatchers.Main) { // launch coroutine in the main thread
146        for (i in 10 downTo 1) { // countdown from 10 to 1
147            hello.text = "Countdown $i ..." // update text
148            delay(500) // wait half a second
149        }
150        hello.text = "Done!"
151    }
152}
153```
154
155> You can get the full code [here](kotlinx-coroutines-javafx/test/guide/example-ui-basic-02.kt).
156
157So, what happens here? Because we are launching coroutine in the main UI context, we can freely update UI from
158inside this coroutine and invoke _suspending functions_ like [delay] at the same time. UI is not frozen
159while `delay` waits, because it does not block the UI thread -- it just suspends the coroutine.
160
161> The corresponding code for Android application is the same.
162  You just need to copy the body of `setup` function into the corresponding function of Android project.
163
164### Cancel UI coroutine
165
166We can keep a reference to the [Job] object that `launch` function returns and use it to cancel
167coroutine when we want to stop it. Let us cancel the coroutine when pinkish circle is clicked:
168
169```kotlin
170fun setup(hello: Text, fab: Circle) {
171    val job = GlobalScope.launch(Dispatchers.Main) { // launch coroutine in the main thread
172        for (i in 10 downTo 1) { // countdown from 10 to 1
173            hello.text = "Countdown $i ..." // update text
174            delay(500) // wait half a second
175        }
176        hello.text = "Done!"
177    }
178    fab.onMouseClicked = EventHandler { job.cancel() } // cancel coroutine on click
179}
180```
181
182> You can get the full code [here](kotlinx-coroutines-javafx/test/guide/example-ui-basic-03.kt).
183
184Now, if the circle is clicked while countdown is still running, the countdown stops.
185Note that [Job.cancel] is completely thread-safe and non-blocking. It just signals the coroutine to cancel
186its job, without waiting for it to actually terminate. It can be invoked from anywhere.
187Invoking it on a coroutine that was already cancelled or has completed does nothing.
188
189> The corresponding line for Android is shown below:
190
191```kotlin
192fab.setOnClickListener { job.cancel() }  // cancel coroutine on click
193```
194
195<!--- CLEAR -->
196
197## Using actors within UI context
198
199In this section we show how UI applications can use actors within their UI context make sure that
200there is no unbounded growth in the number of launched coroutines.
201
202### Extensions for coroutines
203
204Our goal is to write an extension _coroutine builder_ function named `onClick`,
205so that we can perform countdown animation every time when the circle is clicked with this simple code:
206
207```kotlin
208fun setup(hello: Text, fab: Circle) {
209    fab.onClick { // start coroutine when the circle is clicked
210        for (i in 10 downTo 1) { // countdown from 10 to 1
211            hello.text = "Countdown $i ..." // update text
212            delay(500) // wait half a second
213        }
214        hello.text = "Done!"
215    }
216}
217```
218
219<!--- INCLUDE .*/example-ui-actor-([0-9]+).kt -->
220
221Our first implementation for `onClick` just launches a new coroutine on each mouse event and
222passes the corresponding mouse event into the supplied action (just in case we need it):
223
224```kotlin
225fun Node.onClick(action: suspend (MouseEvent) -> Unit) {
226    onMouseClicked = EventHandler { event ->
227        GlobalScope.launch(Dispatchers.Main) {
228            action(event)
229        }
230    }
231}
232```
233
234> You can get the full code [here](kotlinx-coroutines-javafx/test/guide/example-ui-actor-01.kt).
235
236Note that each time the circle is clicked, it starts a new coroutine and they all compete to
237update the text. Try it. It does not look very good. We'll fix it later.
238
239> On Android, the corresponding extension can be written for `View` class, so that the code
240  in `setup` function that is shown above can be used without changes. There is no `MouseEvent`
241  used in OnClickListener on Android, so it is omitted.
242
243```kotlin
244fun View.onClick(action: suspend () -> Unit) {
245    setOnClickListener {
246        GlobalScope.launch(Dispatchers.Main) {
247            action()
248        }
249    }
250}
251```
252
253<!--- CLEAR -->
254
255### At most one concurrent job
256
257We can cancel an active job before starting a new one to ensure that at most one coroutine is animating
258the countdown. However, it is generally not the best idea. The [cancel][Job.cancel] function serves only as a signal
259to abort a coroutine. Cancellation is cooperative and a coroutine may, at the moment, be doing something non-cancellable
260or otherwise ignore a cancellation signal. A better solution is to use an [actor] for tasks that should
261not be performed concurrently. Let us change `onClick` extension implementation:
262
263```kotlin
264fun Node.onClick(action: suspend (MouseEvent) -> Unit) {
265    // launch one actor to handle all events on this node
266    val eventActor = GlobalScope.actor<MouseEvent>(Dispatchers.Main) {
267        for (event in channel) action(event) // pass event to action
268    }
269    // install a listener to offer events to this actor
270    onMouseClicked = EventHandler { event ->
271        eventActor.offer(event)
272    }
273}
274```
275
276> You can get the full code [here](kotlinx-coroutines-javafx/test/guide/example-ui-actor-02.kt).
277
278The key idea that underlies an integration of an actor coroutine and a regular event handler is that
279there is an [offer][SendChannel.offer] function on [SendChannel] that does not wait. It sends an element to the actor immediately,
280if it is possible, or discards an element otherwise. An `offer` actually returns a `Boolean` result which we ignore here.
281
282Try clicking repeatedly on a circle in this version of the code. The clicks are just ignored while the countdown
283animation is running. This happens because the actor is busy with an animation and does not receive from its channel.
284By default, an actor's mailbox is backed by `RendezvousChannel`, whose `offer` operation succeeds only when
285the `receive` is active.
286
287> On Android, there is `View` sent in OnClickListener, so we send the `View` to the actor as a signal.
288  The corresponding extension for `View` class looks like this:
289
290```kotlin
291fun View.onClick(action: suspend (View) -> Unit) {
292    // launch one actor
293    val eventActor = GlobalScope.actor<View>(Dispatchers.Main) {
294        for (event in channel) action(event)
295    }
296    // install a listener to activate this actor
297    setOnClickListener {
298        eventActor.offer(it)
299    }
300}
301```
302
303<!--- CLEAR -->
304
305
306### Event conflation
307
308Sometimes it is more appropriate to process the most recent event, instead of just ignoring events while we were busy
309processing the previous one.  The [actor] coroutine builder accepts an optional `capacity` parameter that
310controls the implementation of the channel that this actor is using for its mailbox. The description of all
311the available choices is given in documentation of the [`Channel()`][Channel] factory function.
312
313Let us change the code to use a conflated channel by passing [Channel.CONFLATED] capacity value. The
314change is only to the line that creates an actor:
315
316```kotlin
317fun Node.onClick(action: suspend (MouseEvent) -> Unit) {
318    // launch one actor to handle all events on this node
319    val eventActor = GlobalScope.actor<MouseEvent>(Dispatchers.Main, capacity = Channel.CONFLATED) { // <--- Changed here
320        for (event in channel) action(event) // pass event to action
321    }
322    // install a listener to offer events to this actor
323    onMouseClicked = EventHandler { event ->
324        eventActor.offer(event)
325    }
326}
327```
328
329> You can get full JavaFx code [here](kotlinx-coroutines-javafx/test/guide/example-ui-actor-03.kt).
330  On Android you need to update `val eventActor = ...` line from the previous example.
331
332Now, if a circle is clicked while the animation is running, it restarts animation after the end of it. Just once.
333Repeated clicks while the animation is running are _conflated_ and only the most recent event gets to be
334processed.
335
336This is also a desired behaviour for UI applications that have to react to incoming high-frequency
337event streams by updating their UI based on the most recently received update. A coroutine that is using
338`ConflatedChannel` avoids delays that are usually introduced by buffering of events.
339
340You can experiment with `capacity` parameter in the above line to see how it affects the behaviour of the code.
341Setting `capacity = Channel.UNLIMITED` creates a coroutine with `LinkedListChannel` mailbox that buffers all
342events. In this case, the animation runs as many times as the circle is clicked.
343
344## Blocking operations
345
346This section explains how to use UI coroutines with thread-blocking operations.
347
348### The problem of UI freezes
349
350It would have been great if all APIs out there were written as suspending functions that never blocks an
351execution thread. However, it is quite often not the case. Sometimes you need to do a CPU-consuming computation
352or just need to invoke some 3rd party APIs for network access, for example, that blocks the invoker thread.
353You cannot do that from the main UI thread nor from the UI-confined coroutine directly, because that would
354block the main UI thread and cause the freeze up of the UI.
355
356<!--- INCLUDE .*/example-ui-blocking-([0-9]+).kt
357fun Node.onClick(action: suspend (MouseEvent) -> Unit) {
358    val eventActor = GlobalScope.actor<MouseEvent>(Dispatchers.Main, capacity = Channel.CONFLATED) {
359        for (event in channel) action(event) // pass event to action
360    }
361    onMouseClicked = EventHandler { event ->
362        eventActor.offer(event)
363    }
364}
365-->
366
367The following example illustrates the problem. We are going to use `onClick` extension with UI-confined
368event-conflating actor from the last section to process the last click in the main UI thread.
369For this example, we are going to
370perform naive computation of [Fibonacci numbers](https://en.wikipedia.org/wiki/Fibonacci_number):
371
372```kotlin
373fun fib(x: Int): Int =
374    if (x <= 1) x else fib(x - 1) + fib(x - 2)
375```
376
377We'll be computing larger and larger Fibonacci number each time the circle is clicked.
378To make the UI freeze more obvious, there is also a fast counting animation that is always running
379and is constantly updating the text in the main UI dispatcher:
380
381```kotlin
382fun setup(hello: Text, fab: Circle) {
383    var result = "none" // the last result
384    // counting animation
385    GlobalScope.launch(Dispatchers.Main) {
386        var counter = 0
387        while (true) {
388            hello.text = "${++counter}: $result"
389            delay(100) // update the text every 100ms
390        }
391    }
392    // compute the next fibonacci number of each click
393    var x = 1
394    fab.onClick {
395        result = "fib($x) = ${fib(x)}"
396        x++
397    }
398}
399```
400
401> You can get full JavaFx code [here](kotlinx-coroutines-javafx/test/guide/example-ui-blocking-01.kt).
402  You can just copy the `fib` function and the body of the `setup` function to your Android project.
403
404Try clicking on the circle in this example. After around 30-40th click our naive computation is going to become
405quite slow and you would immediately see how the main UI thread freezes, because the animation stops running
406during UI freeze.
407
408### Structured concurrency, lifecycle and coroutine parent-child hierarchy
409
410A typical UI application has a number of elements with a lifecycle. Windows, UI controls, activities, views, fragments
411and other visual elements are created and destroyed. A long-running coroutine, performing some IO or a background
412computation, can retain references to the corresponding UI elements for longer than it is needed, preventing garbage
413collection of the whole trees of UI objects that were already destroyed and will not be displayed anymore.
414
415The natural solution to this problem is to associate a [CoroutineScope] object with each UI object that has a
416lifecycle and create all the coroutines in the context of this scope.
417For the sake of simplicity, [MainScope()] factory can be used. It automatically provides `Dispatchers.Main` and
418a parent job for all the children coroutines.
419
420For example, in Android application an `Activity` is initially _created_ and is _destroyed_ when it is no longer
421needed and when its memory must be released. A natural solution is to attach an
422instance of a `CoroutineScope` to an instance of an `Activity`:
423
424<!--- CLEAR -->
425
426```kotlin
427class MainActivity : AppCompatActivity() {
428    private val scope = MainScope()
429
430    override fun onDestroy() {
431        super.onDestroy()
432        scope.cancel()
433    }
434
435    fun asyncShowData() = scope.launch { // Is invoked in UI context with Activity's scope as a parent
436        // actual implementation
437    }
438
439    suspend fun showIOData() {
440        val data = withContext(Dispatchers.IO) {
441            // compute data in background thread
442        }
443        withContext(Dispatchers.Main) {
444            // Show data in UI
445        }
446    }
447}
448```
449
450Every coroutine launched from within a `MainActivity` has its job as a parent and is immediately cancelled when
451activity is destroyed.
452
453> Note, that Android has first-party support for coroutine scope in all entities with the lifecycle.
454See [the corresponding documentation](https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope).
455
456Parent-child relation between jobs forms a hierarchy. A coroutine that performs some background job on behalf of
457the activity can create further children coroutines. The whole tree of coroutines gets cancelled
458when the parent job is cancelled. An example of that is shown in the
459["Children of a coroutine"](../docs/coroutine-context-and-dispatchers.md#children-of-a-coroutine) section of the guide to coroutines.
460
461<!--- CLEAR -->
462
463### Blocking operations
464
465The fix for the blocking operations on the main UI thread is quite straightforward with coroutines. We'll
466convert our "blocking" `fib` function to a non-blocking suspending function that runs the computation in
467the background thread by using [withContext] function to change its execution context to [Dispatchers.Default] that is
468backed by the background pool of threads.
469Notice, that `fib` function is now marked with `suspend` modifier. It does not block the coroutine that
470it is invoked from anymore, but suspends its execution when the computation in the background thread is working:
471
472<!--- INCLUDE .*/example-ui-blocking-0[23].kt
473
474fun setup(hello: Text, fab: Circle) {
475    var result = "none" // the last result
476    // counting animation
477    GlobalScope.launch(Dispatchers.Main) {
478        var counter = 0
479        while (true) {
480            hello.text = "${++counter}: $result"
481            delay(100) // update the text every 100ms
482        }
483    }
484    // compute next fibonacci number of each click
485    var x = 1
486    fab.onClick {
487        result = "fib($x) = ${fib(x)}"
488        x++
489    }
490}
491-->
492
493```kotlin
494suspend fun fib(x: Int): Int = withContext(Dispatchers.Default) {
495    if (x <= 1) x else fib(x - 1) + fib(x - 2)
496}
497```
498
499> You can get the full code [here](kotlinx-coroutines-javafx/test/guide/example-ui-blocking-02.kt).
500
501You can run this code and verify that UI is not frozen while large Fibonacci numbers are being computed.
502However, this code computes `fib` somewhat slower, because every recursive call to `fib` goes via `withContext`. This is
503not a big problem in practice, because `withContext` is smart enough to check that the coroutine is already running
504in the required context and avoids overhead of dispatching coroutine to a different thread again. It is an
505overhead nonetheless, which is visible on this primitive code that does nothing else, but only adds integers
506in between invocations to `withContext`. For some more substantial code, the overhead of an extra `withContext` invocation is
507not going to be significant.
508
509Still, this particular `fib` implementation can be made to run as fast as before, but in the background thread, by renaming
510the original `fib` function to `fibBlocking` and defining `fib` with `withContext` wrapper on top of `fibBlocking`:
511
512```kotlin
513suspend fun fib(x: Int): Int = withContext(Dispatchers.Default) {
514    fibBlocking(x)
515}
516
517fun fibBlocking(x: Int): Int =
518    if (x <= 1) x else fibBlocking(x - 1) + fibBlocking(x - 2)
519```
520
521> You can get the full code [here](kotlinx-coroutines-javafx/test/guide/example-ui-blocking-03.kt).
522
523You can now enjoy full-speed naive Fibonacci computation without blocking the main UI thread.
524All we need is `withContext(Dispatchers.Default)`.
525
526Note that because the `fib` function is invoked from the single actor in our code, there is at most one concurrent
527computation of it at any given time, so this code has a natural limit on the resource utilization.
528It can saturate at most one CPU core.
529
530## Advanced topics
531
532This section covers various advanced topics.
533
534### Starting coroutine in UI event handlers without dispatch
535
536Let us write the following code in `setup` to visualize the order of execution when coroutine is launched
537from the UI thread:
538
539<!--- CLEAR -->
540
541```kotlin
542fun setup(hello: Text, fab: Circle) {
543    fab.onMouseClicked = EventHandler {
544        println("Before launch")
545        GlobalScope.launch(Dispatchers.Main) {
546            println("Inside coroutine")
547            delay(100)
548            println("After delay")
549        }
550        println("After launch")
551    }
552}
553```
554
555> You can get full JavaFx code [here](kotlinx-coroutines-javafx/test/guide/example-ui-advanced-01.kt).
556
557When we start this code and click on a pinkish circle, the following messages are printed to the console:
558
559```text
560Before launch
561After launch
562Inside coroutine
563After delay
564```
565
566As you can see, execution immediately continues after [launch], while the coroutine gets posted onto the main UI thread
567for execution later. All UI dispatchers in `kotlinx.coroutines` are implemented this way. Why so?
568
569Basically, the choice here is between "JS-style" asynchronous approach (async actions
570are always postponed to be executed later in the event dispatch thread) and "C#-style" approach
571(async actions are executed in the invoker thread until the first suspension point).
572While, C# approach seems to be more efficient, it ends up with recommendations like
573"use `yield` if you need to ....". This is error-prone. JS-style approach is more consistent
574and does not require programmers to think about whether they need to yield or not.
575
576However, in this particular case when coroutine is started from an event handler and there is no other code around it,
577this extra dispatch does indeed add an extra overhead without bringing any additional value.
578In this case an optional [CoroutineStart] parameter to [launch], [async] and [actor] coroutine builders
579can be used for performance optimization.
580Setting it to the value of [CoroutineStart.UNDISPATCHED] has the effect of starting to execute
581coroutine immediately until its first suspension point as the following example shows:
582
583```kotlin
584fun setup(hello: Text, fab: Circle) {
585    fab.onMouseClicked = EventHandler {
586        println("Before launch")
587        GlobalScope.launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED) { // <--- Notice this change
588            println("Inside coroutine")
589            delay(100)                            // <--- And this is where coroutine suspends
590            println("After delay")
591        }
592        println("After launch")
593    }
594}
595```
596
597> You can get full JavaFx code [here](kotlinx-coroutines-javafx/test/guide/example-ui-advanced-02.kt).
598
599It prints the following messages on click, confirming that code in the coroutine starts to execute immediately:
600
601```text
602Before launch
603Inside coroutine
604After launch
605After delay
606```
607
608<!--- MODULE kotlinx-coroutines-core -->
609<!--- INDEX kotlinx.coroutines -->
610[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html
611[delay]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html
612[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html
613[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html
614[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
615[MainScope()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-main-scope.html
616[withContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html
617[Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html
618[CoroutineStart]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/index.html
619[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html
620[CoroutineStart.UNDISPATCHED]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/-u-n-d-i-s-p-a-t-c-h-e-d.html
621<!--- INDEX kotlinx.coroutines.channels -->
622[actor]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/actor.html
623[SendChannel.offer]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/offer.html
624[SendChannel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-send-channel/index.html
625[Channel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel/index.html
626[Channel.CONFLATED]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel/-c-o-n-f-l-a-t-e-d.html
627<!--- MODULE kotlinx-coroutines-javafx -->
628<!--- INDEX kotlinx.coroutines.javafx -->
629[kotlinx.coroutines.Dispatchers.JavaFx]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-javafx/kotlinx.coroutines.javafx/kotlinx.coroutines.-dispatchers/-java-fx.html
630<!--- MODULE kotlinx-coroutines-android -->
631<!--- INDEX kotlinx.coroutines.android -->
632<!--- END -->
633