Guide on coroutines expanded (cancellation is covered) and linked from README page
diff --git a/README.md b/README.md
index 9629e10..848e281 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,18 @@
# kotlinx.coroutines
Library support for Kotlin coroutines. This is a companion version for Kotlin 1.1.0-beta-18 release.
-It contains worked-out implementation of coroutine builders, suspending functions, and contexts that are
-used as examples in
-[Kotlin coroutines design document](https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md)
-See [change log](CHANGES.md) for a summary of changes between releases.
-
-It consists of the following modules:
+## Modules and features
* `kotlinx-coroutines-core` module with core primitives to work with coroutines. It is designed to work on any JDK6+ and Android
and contains the following main pieces:
- * `launch(context) { ... }` to start a coroutine in the given context.
+ * `launch(context) { ... }` to start a coroutine in the given context and get reference to its `Job`.
* `run(context) { ... }` to switch to a different context inside a coroutine.
* `runBlocking { ... }` to use asynchronous Kotlin APIs from a thread-blocking code.
- * `defer(context) { ... }` to get a deferred result of coroutine execution in a non-blocking way.
+ * `defer(context) { ... }` to get a deferred result of coroutine execution in a non-blocking way
+ via a light-weight future interface called `Deferred`.
* `delay(...)` for a non-blocking sleep in coroutines.
- * `Here` and `CommonPool` contexts, `context` or a parent coroutine.
+ * `CommonPool` and `Here` contexts, `context` or a parent coroutine.
* `newSingleThreadContext(...)` and `newFixedThreadPoolContext(...)` functions,
`Executor.toCoroutineDispatcher()` extension.
* Cancellation support with `Job` interface and `suspendCancellableCoroutine` helper function.
@@ -38,6 +34,12 @@
[RxJava](https://github.com/ReactiveX/RxJava) with imperative coroutines and consume their values
from inside coroutines. It is in very basic form now (example-only, not even close to production use)
+## References and documentation
+
+* [Coroutines guide](coroutines-guide.md)
+* [Change log](CHANGES.md)
+* [Coroutines design document](https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md)
+
## Using in your projects
> Note that these libraries are experimental and are subject to change.
diff --git a/coroutines-guide.md b/coroutines-guide.md
new file mode 100644
index 0000000..4f70671
--- /dev/null
+++ b/coroutines-guide.md
@@ -0,0 +1,242 @@
+# Guide to kotlinx.coroutines by example
+
+This is a short guide on core features of `kotlinx.coroutines` with a series of examples.
+
+## Your first coroutine
+
+Run the following code:
+
+```kotlin
+fun main(args: Array<String>) {
+ launch(CommonPool) { // create new coroutine in common thread pool
+ delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
+ println("World!") // print after delay
+ }
+ println("Hello,") // main function continues while coroutine is delayed
+ Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
+}
+```
+
+> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-01.kt)
+
+Run this code:
+
+```
+Hello,
+World!
+```
+
+Essentially, coroutines are light-weight threads. You can achieve the same result replacing
+`launch(CommonPool) { ... }` with `thread { ... }` and `delay(...)` with `Thread.sleep(...)`. Try it.
+
+If you start by replacing `launch(CommonPool)` by `thread`, the compiler produces the following error:
+
+```
+Error: Kotlin: Suspend functions are only allowed to be called from a coroutine or another suspend function
+```
+
+That is because `delay` is a special _suspending function_ that does not block a thread, but _suspends_
+coroutine and it can be only used from a coroutine.
+
+## Bridging blocking and non-blocking worlds
+
+The first example mixes _non-blocking_ `delay(...)` and _blocking_ `Thread.sleep(...)` in the same
+code of `main` function. It is easy to get lost. Let's cleanly separate blocking and non-blocking
+worlds by using `runBlocking { ... }`:
+
+```kotlin
+fun main(args: Array<String>) = runBlocking<Unit> { // start main coroutine
+ launch(CommonPool) { // create new coroutine in common thread pool
+ delay(1000L)
+ println("World!")
+ }
+ println("Hello,") // main coroutine continues while child is delayed
+ delay(2000L) // non-blocking delay for 2 seconds to keep JVM alive
+}
+```
+
+> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-02.kt)
+
+The result is the same, but this code uses only non-blocking `delay`.
+
+`runBlocking { ... }` works as an adaptor that is used here to start the top-level main coroutine.
+The regular code outside of `runBlocking` _blocks_, until the coroutine inside `runBlocking` is active.
+
+This is also a way to write unit-tests for suspending functions:
+
+```kotlin
+class MyTest {
+ @Test
+ fun testMySuspendingFunction() = runBlocking<Unit> {
+ // here we can use suspending functions using any assertion style that we like
+ }
+}
+```
+
+## Waiting for a job
+
+Delaying for a time while a _child_ coroutine is working is not a good approach. Let's explicitly
+wait (in a non-blocking way) until the other coroutine that we have launched is complete:
+
+```kotlin
+fun main(args: Array<String>) = runBlocking<Unit> {
+ val job = launch(CommonPool) { // create new coroutine and keep a reference to its Job
+ delay(1000L)
+ println("World!")
+ }
+ println("Hello,")
+ job.join() // wait until child coroutine completes
+}
+```
+
+> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-03.kt)
+
+Now the result is still the same, but the code of the main coroutine is not tied to the duration of
+the child coroutine in any way. Much better.
+
+## Extract function refactoring
+
+Let's extract the block of code inside `launch(CommonPool} { ... }` into a separate function. When you
+perform "Extract function" refactoring on this code you get a new function with `suspend` modifier.
+That is your first _suspending function_. Suspending functions can be used inside coroutines
+just like regular functions, but their additional feature is that they can, in turn,
+use other suspending functions, like `delay` in this example, to _suspend_ execution of a coroutine.
+
+```kotlin
+fun main(args: Array<String>) = runBlocking<Unit> {
+ val job = launch(CommonPool) { doWorld() }
+ println("Hello,")
+ job.join()
+}
+
+// this is your first suspending function
+suspend fun doWorld() {
+ delay(1000L)
+ println("World!")
+}
+```
+
+> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-04.kt)
+
+## Coroutines ARE light-weight
+
+Run the following code:
+
+```kotlin
+fun main(args: Array<String>) = runBlocking<Unit> {
+ val jobs = List(100_000) { // create a lot of coroutines and list their jobs
+ launch(CommonPool) {
+ delay(1000L)
+ print(".")
+ }
+ }
+ jobs.forEach { it.join() } // wait for all jobs to complete
+}
+```
+
+> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-05.kt)
+
+It starts 100K coroutines and, after a second, each coroutine prints a dot.
+Now, try that with threads. What would happen? (Most likely your code will produce some sort of out-of-memory error)
+
+## Coroutines are like daemon threads
+
+The following code launches a long-running coroutine that prints "I'm sleeping" twice a second and then
+returns from the main thread after some delay:
+
+```kotlin
+fun main(args: Array<String>) = runBlocking<Unit> {
+ launch(CommonPool) {
+ repeat(1000) { i ->
+ println("I'm sleeping $i ...")
+ delay(500L)
+ }
+ }
+ delay(1300L) // just quit after delay
+}
+```
+
+> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-06.kt)
+
+You can run and see that it prints three lines and terminates:
+
+```
+I'm sleeping 0 ...
+I'm sleeping 1 ...
+I'm sleeping 2 ...
+```
+
+Active coroutines do not keep the process alive. They are like daemon threads.
+
+## Cancelling coroutine execution
+
+In small application the return from "main" method might sound like a good idea to get all coroutines
+implicitly terminated. In a larger, long-running application, you need finer-grained control.
+The `launch` function returns a `Job` that can be used to cancel running coroutine:
+
+```kotlin
+fun main(args: Array<String>) = runBlocking<Unit> {
+ val job = launch(CommonPool) {
+ repeat(1000) { i ->
+ println("I'm sleeping $i ...")
+ delay(500L)
+ }
+ }
+ delay(1300L) // delay a bit
+ println("I'm tired of waiting!")
+ job.cancel() // cancels the job
+ delay(1300L) // delay a bit to ensure it was cancelled indeed
+ println("Now I can quit.")
+}
+```
+
+> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-07.kt)
+
+## Cancellation is cooperative
+
+Coroutine cancellation is _cooperative_. A coroutine code has to cooperate be cancellable.
+All the suspending functions in `kotlinx.coroutines` are _cancellable_. They check for cancellation of
+coroutine and throw `CancellationException` when cancelled. However, if a coroutine is working in
+a computation and does not check for cancellation, then it cannot be cancelled, like the following
+example shows:
+
+```kotlin
+fun main(args: Array<String>) = runBlocking<Unit> {
+ val job = launch(CommonPool) {
+ var nextPrintTime = 0L
+ var i = 0
+ while (true) { // computation loop
+ val currentTime = System.currentTimeMillis()
+ if (currentTime >= nextPrintTime) {
+ println("I'm sleeping ${i++} ...")
+ nextPrintTime = currentTime + 500L
+ }
+ }
+ }
+ delay(1300L) // delay a bit
+ println("I'm tired of waiting!")
+ job.cancel() // cancels the job
+ delay(1300L) // delay a bit to see if it was cancelled....
+ println("Now I can quit.")
+}
+```
+
+> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-08.kt)
+
+Run it to see that it continues to print "I'm sleeping" even after cancellation.
+
+## Making computation code cancellable
+
+There are two approaches to making computation code cancellable. The first one is to periodically
+invoke any suspending function. There is a `yield` function that is a good choice for that purpose.
+The other one is to explicitly check the cancellation status. The following example demonstrates
+the later approach.
+
+Replace `while (true)` in the previous example with `while (isActive)` and rerun it.
+
+> You can get full code [here](kotlinx-coroutines-core/src/test/kotlin/examples/example-09.kt)
+
+As you can see, now this loop can be cancelled. `isActive` is a property that is available inside
+the code of coroutines via `CoroutineScope` object.
+
+
diff --git a/kotlinx-coroutines-core/src/test/kotlin/examples/tutorial-example-1.kt b/kotlinx-coroutines-core/src/test/kotlin/examples/example-01.kt
similarity index 77%
rename from kotlinx-coroutines-core/src/test/kotlin/examples/tutorial-example-1.kt
rename to kotlinx-coroutines-core/src/test/kotlin/examples/example-01.kt
index 0708127..6d61824 100644
--- a/kotlinx-coroutines-core/src/test/kotlin/examples/tutorial-example-1.kt
+++ b/kotlinx-coroutines-core/src/test/kotlin/examples/example-01.kt
@@ -1,11 +1,11 @@
package examples
-import kotlinx.coroutines.experimental.Here
+import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.launch
fun main(args: Array<String>) {
- launch(Here) { // create new coroutine without an explicit threading policy
+ launch(CommonPool) { // create new coroutine in common thread pool
delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
println("World!") // print after delay
}
diff --git a/kotlinx-coroutines-core/src/test/kotlin/examples/tutorial-example-2.kt b/kotlinx-coroutines-core/src/test/kotlin/examples/example-02.kt
similarity index 64%
rename from kotlinx-coroutines-core/src/test/kotlin/examples/tutorial-example-2.kt
rename to kotlinx-coroutines-core/src/test/kotlin/examples/example-02.kt
index b97ffd3..5d241a1 100644
--- a/kotlinx-coroutines-core/src/test/kotlin/examples/tutorial-example-2.kt
+++ b/kotlinx-coroutines-core/src/test/kotlin/examples/example-02.kt
@@ -1,12 +1,12 @@
package examples
-import kotlinx.coroutines.experimental.Here
+import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.runBlocking
-fun main(args: Array<String>) = runBlocking { // start main coroutine
- launch(Here) { // create new coroutine without an explicit threading policy
+fun main(args: Array<String>) = runBlocking<Unit> { // start main coroutine
+ launch(CommonPool) { // create new coroutine in common thread pool
delay(1000L)
println("World!")
}
diff --git a/kotlinx-coroutines-core/src/test/kotlin/examples/example-03.kt b/kotlinx-coroutines-core/src/test/kotlin/examples/example-03.kt
new file mode 100644
index 0000000..32d73c1
--- /dev/null
+++ b/kotlinx-coroutines-core/src/test/kotlin/examples/example-03.kt
@@ -0,0 +1,12 @@
+package examples
+
+import kotlinx.coroutines.experimental.*
+
+fun main(args: Array<String>) = runBlocking<Unit> {
+ val job = launch(CommonPool) { // create new coroutine and keep a reference to its Job
+ delay(1000L)
+ println("World!")
+ }
+ println("Hello,")
+ job.join() // wait until child coroutine completes
+}
diff --git a/kotlinx-coroutines-core/src/test/kotlin/examples/tutorial-example-4.kt b/kotlinx-coroutines-core/src/test/kotlin/examples/example-04.kt
similarity index 67%
rename from kotlinx-coroutines-core/src/test/kotlin/examples/tutorial-example-4.kt
rename to kotlinx-coroutines-core/src/test/kotlin/examples/example-04.kt
index f302d18..211c5bc 100644
--- a/kotlinx-coroutines-core/src/test/kotlin/examples/tutorial-example-4.kt
+++ b/kotlinx-coroutines-core/src/test/kotlin/examples/example-04.kt
@@ -2,8 +2,8 @@
import kotlinx.coroutines.experimental.*
-fun main(args: Array<String>) = runBlocking {
- val job = launch(Here) { doWorld() }
+fun main(args: Array<String>) = runBlocking<Unit> {
+ val job = launch(CommonPool) { doWorld() }
println("Hello,")
job.join()
}
diff --git a/kotlinx-coroutines-core/src/test/kotlin/examples/tutorial-example-5.kt b/kotlinx-coroutines-core/src/test/kotlin/examples/example-05.kt
similarity index 76%
rename from kotlinx-coroutines-core/src/test/kotlin/examples/tutorial-example-5.kt
rename to kotlinx-coroutines-core/src/test/kotlin/examples/example-05.kt
index 9995e90..c79b217 100644
--- a/kotlinx-coroutines-core/src/test/kotlin/examples/tutorial-example-5.kt
+++ b/kotlinx-coroutines-core/src/test/kotlin/examples/example-05.kt
@@ -2,9 +2,9 @@
import kotlinx.coroutines.experimental.*
-fun main(args: Array<String>) = runBlocking {
+fun main(args: Array<String>) = runBlocking<Unit> {
val jobs = List(100_000) { // create a lot of coroutines and list their jobs
- launch(Here) {
+ launch(CommonPool) {
delay(1000L)
print(".")
}
diff --git a/kotlinx-coroutines-core/src/test/kotlin/examples/example-06.kt b/kotlinx-coroutines-core/src/test/kotlin/examples/example-06.kt
new file mode 100644
index 0000000..2efb5cf
--- /dev/null
+++ b/kotlinx-coroutines-core/src/test/kotlin/examples/example-06.kt
@@ -0,0 +1,16 @@
+package examples
+
+import kotlinx.coroutines.experimental.CommonPool
+import kotlinx.coroutines.experimental.delay
+import kotlinx.coroutines.experimental.launch
+import kotlinx.coroutines.experimental.runBlocking
+
+fun main(args: Array<String>) = runBlocking<Unit> {
+ launch(CommonPool) {
+ repeat(1000) { i ->
+ println("I'm sleeping $i ...")
+ delay(500L)
+ }
+ }
+ delay(1300L) // just quit after delay
+}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/src/test/kotlin/examples/example-07.kt b/kotlinx-coroutines-core/src/test/kotlin/examples/example-07.kt
new file mode 100644
index 0000000..5504410
--- /dev/null
+++ b/kotlinx-coroutines-core/src/test/kotlin/examples/example-07.kt
@@ -0,0 +1,20 @@
+package examples
+
+import kotlinx.coroutines.experimental.CommonPool
+import kotlinx.coroutines.experimental.delay
+import kotlinx.coroutines.experimental.launch
+import kotlinx.coroutines.experimental.runBlocking
+
+fun main(args: Array<String>) = runBlocking<Unit> {
+ val job = launch(CommonPool) {
+ repeat(1000) { i ->
+ println("I'm sleeping $i ...")
+ delay(500L)
+ }
+ }
+ delay(1300L) // delay a bit
+ println("I'm tired of waiting!")
+ job.cancel() // cancels the job
+ delay(1300L) // delay a bit to ensure it was cancelled indeed
+ println("Now I can quit.")
+}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/src/test/kotlin/examples/example-08.kt b/kotlinx-coroutines-core/src/test/kotlin/examples/example-08.kt
new file mode 100644
index 0000000..09c0962
--- /dev/null
+++ b/kotlinx-coroutines-core/src/test/kotlin/examples/example-08.kt
@@ -0,0 +1,25 @@
+package examples
+
+import kotlinx.coroutines.experimental.CommonPool
+import kotlinx.coroutines.experimental.delay
+import kotlinx.coroutines.experimental.launch
+import kotlinx.coroutines.experimental.runBlocking
+
+fun main(args: Array<String>) = runBlocking<Unit> {
+ val job = launch(CommonPool) {
+ var nextPrintTime = 0L
+ var i = 0
+ while (true) { // computation loop
+ val currentTime = System.currentTimeMillis()
+ if (currentTime >= nextPrintTime) {
+ println("I'm sleeping ${i++} ...")
+ nextPrintTime = currentTime + 500L
+ }
+ }
+ }
+ delay(1300L) // delay a bit
+ println("I'm tired of waiting!")
+ job.cancel() // cancels the job
+ delay(1300L) // delay a bit to see if it was cancelled....
+ println("Now I can quit.")
+}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/src/test/kotlin/examples/example-09.kt b/kotlinx-coroutines-core/src/test/kotlin/examples/example-09.kt
new file mode 100644
index 0000000..cd745f3
--- /dev/null
+++ b/kotlinx-coroutines-core/src/test/kotlin/examples/example-09.kt
@@ -0,0 +1,25 @@
+package examples
+
+import kotlinx.coroutines.experimental.CommonPool
+import kotlinx.coroutines.experimental.delay
+import kotlinx.coroutines.experimental.launch
+import kotlinx.coroutines.experimental.runBlocking
+
+fun main(args: Array<String>) = runBlocking<Unit> {
+ val job = launch(CommonPool) {
+ var nextPrintTime = 0L
+ var i = 0
+ while (isActive) { // cancellable computation loop
+ val currentTime = System.currentTimeMillis()
+ if (currentTime >= nextPrintTime) {
+ println("I'm sleeping ${i++} ...")
+ nextPrintTime = currentTime + 500L
+ }
+ }
+ }
+ delay(1300L) // delay a bit
+ println("I'm tired of waiting!")
+ job.cancel() // cancels the job
+ delay(1300L) // delay a bit to see if it was cancelled....
+ println("Now I can quit.")
+}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/src/test/kotlin/examples/tutorial-example-3.kt b/kotlinx-coroutines-core/src/test/kotlin/examples/tutorial-example-3.kt
deleted file mode 100644
index 6239b6a..0000000
--- a/kotlinx-coroutines-core/src/test/kotlin/examples/tutorial-example-3.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package examples
-
-import kotlinx.coroutines.experimental.*
-
-fun main(args: Array<String>) = runBlocking {
- val job = launch(Here) { // create new coroutine and keep a reference to its Job
- delay(1000L)
- println("World!")
- }
- println("Hello,")
- job.join() // wait until child coroutine completes
-}
diff --git a/kotlinx-coroutines-core/tutorial.md b/kotlinx-coroutines-core/tutorial.md
deleted file mode 100644
index ae2e38d..0000000
--- a/kotlinx-coroutines-core/tutorial.md
+++ /dev/null
@@ -1,140 +0,0 @@
-# kotlinx.coroutines core tutorial
-
-This is a DRAFT of a very short tutorial on core features of `kotlinx.coroutines`.
-
-## Your first coroutine
-
-Run the following code:
-
-```kotlin
-fun main(args: Array<String>) {
- launch(Here) { // create new coroutine without an explicit threading policy
- delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
- println("World!") // print after delay
- }
- println("Hello,") // main function continues while coroutine is delayed
- Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
-}
-```
-
-> You can get full code with imports [here](src/test/kotlin/examples/tutorial-example-1.kt)
-
-Run this code:
-
-```
-Hello,
-World!
-```
-
-Essentially, coroutines are light-weight threads. You can achieve the same result replacing
-`launch(Here) { ... }` with `thread { ... }` and `delay(...)` with `Thread.sleep(...)`. Try it.
-
-If you start by replacing `launch(Here)` by `thread`, the compiler produces the following error:
-
-```
-Error: Kotlin: Suspend functions are only allowed to be called from a coroutine or another suspend function
-```
-
-That is because `delay` is a special _suspending function_ that does not block a thread, but _suspends_
-coroutine and it can be only used from a coroutine.
-
-## Bridging blocking and non-blocking worlds
-
-The first example mixes _non-blocking_ `delay(...)` and _blocking_ `Thread.sleep(...)` in the same
-code of `main` function. It is easy to get lost. Let's cleanly separate blocking and non-blocking
-worlds by using `runBlocking { ... }`:
-
-```kotlin
-fun main(args: Array<String>) = runBlocking { // start main coroutine
- launch(Here) { // create new coroutine without an explicit threading policy
- delay(1000L)
- println("World!")
- }
- println("Hello,") // main coroutine continues while child is delayed
- delay(2000L) // non-blocking delay for 2 seconds to keep JVM alive
-}
-```
-
-> You can get full code with imports [here](src/test/kotlin/examples/tutorial-example-2.kt)
-
-The result is the same, but this code uses only non-blocking `delay`.
-
-`runBlocking { ... }` works as an adaptor that is used here to start the top-level main coroutine.
-The regular code outside of `runBlocking` _blocks_, until the coroutine inside `runBlocking` is active.
-
-This is also a way to write unit-tests for suspending functions:
-
-```kotlin
-class MyTest {
- @Test
- fun testMySuspendingFunction() = runBlocking {
- // here we can use suspending functions using any assertion style that we like
- }
-}
-```
-
-## Waiting for a job
-
-Delaying for a time while a _child_ coroutine is working is not a good approach. Let's explicitly
-wait (in a non-blocking way) until the other coroutine that we have launched is complete:
-
-```kotlin
-fun main(args: Array<String>) = runBlocking {
- val job = launch(Here) { // create new coroutine and keep a reference to its Job
- delay(1000L)
- println("World!")
- }
- println("Hello,")
- job.join() // wait until child coroutine completes
-}
-```
-
-> You can get full code with imports [here](src/test/kotlin/examples/tutorial-example-3.kt)
-
-Now the result is still the same, but the code of the main coroutine is not tied to the duration of
-the child coroutine in any way. Much better.
-
-## Extract function refactoring
-
-Let's extract the block of code inside `launch(Here} { ... }` into a separate function. When you
-perform "Extract function" refactoring on this code you get a new function with `suspend` modifier.
-That is your first _suspending function_. Suspending functions can be used inside coroutines
-just like regular functions, but their additional feature is that they can, in turn,
-use other suspending functions, like `delay` in this example, to _suspend_ execution of a coroutine.
-
-```kotlin
-fun main(args: Array<String>) = runBlocking {
- val job = launch(Here) { doWorld() }
- println("Hello,")
- job.join()
-}
-
-// this is your first suspending function
-suspend fun doWorld() {
- delay(1000L)
- println("World!")
-}
-```
-
-> You can get full code with imports [here](src/test/kotlin/examples/tutorial-example-4.kt)
-
-## Coroutines ARE light-weight
-
-Run the following code:
-
-```kotlin
-fun main(args: Array<String>) = runBlocking {
- val jobs = List(100_000) { // create a lot of coroutines and list their jobs
- launch(Here) {
- delay(1000L)
- print(".")
- }
- }
- jobs.forEach { it.join() } // wait for all jobs to complete
-}
-```
-
-> You can get full code with imports [here](src/test/kotlin/examples/tutorial-example-5.kt)
-
-It starts 100K coroutines and, after a second, each coroutine prints a dot.
-Now, try that with threads. What would happen? (Most likely your code will produce some sort of out-of-memory error)