diff --git a/coroutines-guide.md b/coroutines-guide.md index 62fa2c17f4..9267efd144 100644 --- a/coroutines-guide.md +++ b/coroutines-guide.md @@ -3,7 +3,7 @@ The main coroutines guide has moved to the [docs folder](docs/coroutines-guide.m ## Table of contents -* [Coroutine basics](docs/basics.md#coroutine-basics) +* [Coroutine Basics](docs/basics.md#coroutine-basics) * [Your first coroutine](docs/basics.md#your-first-coroutine) * [Bridging blocking and non-blocking worlds](docs/basics.md#bridging-blocking-and-non-blocking-worlds) * [Waiting for a job](docs/basics.md#waiting-for-a-job) @@ -13,7 +13,7 @@ The main coroutines guide has moved to the [docs folder](docs/coroutines-guide.m * [Coroutines ARE light-weight](docs/basics.md#coroutines-are-light-weight) * [Global coroutines are like daemon threads](docs/basics.md#global-coroutines-are-like-daemon-threads) -* [Cancellation and timeouts](docs/cancellation-and-timeouts.md#cancellation-and-timeouts) +* [Cancellation and Timeouts](docs/cancellation-and-timeouts.md#cancellation-and-timeouts) * [Cancelling coroutine execution](docs/cancellation-and-timeouts.md#cancelling-coroutine-execution) * [Cancellation is cooperative](docs/cancellation-and-timeouts.md#cancellation-is-cooperative) * [Making computation code cancellable](docs/cancellation-and-timeouts.md#making-computation-code-cancellable) @@ -21,14 +21,14 @@ The main coroutines guide has moved to the [docs folder](docs/coroutines-guide.m * [Run non-cancellable block](docs/cancellation-and-timeouts.md#run-non-cancellable-block) * [Timeout](docs/cancellation-and-timeouts.md#timeout) -* [Composing suspending functions](docs/composing-suspending-functions.md#composing-suspending-functions) +* [Composing Suspending Functions](docs/composing-suspending-functions.md#composing-suspending-functions) * [Sequential by default](docs/composing-suspending-functions.md#sequential-by-default) * [Concurrent using async](docs/composing-suspending-functions.md#concurrent-using-async) * [Lazily started async](docs/composing-suspending-functions.md#lazily-started-async) * [Async-style functions](docs/composing-suspending-functions.md#async-style-functions) * [Structured concurrency with async](docs/composing-suspending-functions.md#structured-concurrency-with-async) -* [Coroutine context and dispatchers](docs/coroutine-context-and-dispatchers.md#coroutine-context-and-dispatchers) +* [Coroutine Context and Dispatchers](docs/coroutine-context-and-dispatchers.md#coroutine-context-and-dispatchers) * [Dispatchers and threads](docs/coroutine-context-and-dispatchers.md#dispatchers-and-threads) * [Unconfined vs confined dispatcher](docs/coroutine-context-and-dispatchers.md#unconfined-vs-confined-dispatcher) * [Debugging coroutines and threads](docs/coroutine-context-and-dispatchers.md#debugging-coroutines-and-threads) @@ -40,16 +40,45 @@ The main coroutines guide has moved to the [docs folder](docs/coroutines-guide.m * [Combining context elements](docs/coroutine-context-and-dispatchers.md#combining-context-elements) * [Coroutine scope](docs/coroutine-context-and-dispatchers.md#coroutine-scope) * [Thread-local data](docs/coroutine-context-and-dispatchers.md#thread-local-data) - -* [Exception handling](docs/exception-handling.md#exception-handling) - * [Exception propagation](docs/exception-handling.md#exception-propagation) - * [CoroutineExceptionHandler](docs/exception-handling.md#coroutineexceptionhandler) - * [Cancellation and exceptions](docs/exception-handling.md#cancellation-and-exceptions) - * [Exceptions aggregation](docs/exception-handling.md#exceptions-aggregation) -* [Supervision](docs/exception-handling.md#supervision) - * [Supervision job](docs/exception-handling.md#supervision-job) - * [Supervision scope](docs/exception-handling.md#supervision-scope) - * [Exceptions in supervised coroutines](docs/exception-handling.md#exceptions-in-supervised-coroutines) + +* [Asynchronous Flow](docs/flow.md#asynchronous-flow) + * [Representing multiple values](docs/flow.md#representing-multiple-values) + * [Sequences](docs/flow.md#sequences) + * [Suspending functions](docs/flow.md#suspending-functions) + * [Flows](docs/flow.md#flows) + * [Flows are cold](docs/flow.md#flows-are-cold) + * [Flow cancellation](docs/flow.md#flow-cancellation) + * [Flow builders](docs/flow.md#flow-builders) + * [Intermediate flow operators](docs/flow.md#intermediate-flow-operators) + * [Transform operator](docs/flow.md#transform-operator) + * [Size-limiting operators](docs/flow.md#size-limiting-operators) + * [Terminal flow operators](docs/flow.md#terminal-flow-operators) + * [Flows are sequential](docs/flow.md#flows-are-sequential) + * [Flow context](docs/flow.md#flow-context) + * [Wrong emission withContext](docs/flow.md#wrong-emission-withcontext) + * [flowOn operator](docs/flow.md#flowon-operator) + * [Buffering](docs/flow.md#buffering) + * [Conflation](docs/flow.md#conflation) + * [Processing the latest value](docs/flow.md#processing-the-latest-value) + * [Composing multiple flows](docs/flow.md#composing-multiple-flows) + * [Zip](docs/flow.md#zip) + * [Combine](docs/flow.md#combine) + * [Flattening flows](docs/flow.md#flattening-flows) + * [flatMapConcat](docs/flow.md#flatmapconcat) + * [flatMapMerge](docs/flow.md#flatmapmerge) + * [flatMapLatest](docs/flow.md#flatmaplatest) + * [Flow exceptions](docs/flow.md#flow-exceptions) + * [Collector try and catch](docs/flow.md#collector-try-and-catch) + * [Everything is caught](docs/flow.md#everything-is-caught) + * [Exception transparency](docs/flow.md#exception-transparency) + * [Transparent catch](docs/flow.md#transparent-catch) + * [Catching declaratively](docs/flow.md#catching-declaratively) + * [Flow completion](docs/flow.md#flow-completion) + * [Imperative finally block](docs/flow.md#imperative-finally-block) + * [Declarative handling](docs/flow.md#declarative-handling) + * [Upstream exceptions only](docs/flow.md#upstream-exceptions-only) + * [Imperative versus declarative](docs/flow.md#imperative-versus-declarative) + * [Launching flow](docs/flow.md#launching-flow) * [Channels](docs/channels.md#channels) * [Channel basics](docs/channels.md#channel-basics) @@ -62,6 +91,16 @@ The main coroutines guide has moved to the [docs folder](docs/coroutines-guide.m * [Buffered channels](docs/channels.md#buffered-channels) * [Channels are fair](docs/channels.md#channels-are-fair) * [Ticker channels](docs/channels.md#ticker-channels) + +* [Exception Handling](docs/exception-handling.md#exception-handling) + * [Exception propagation](docs/exception-handling.md#exception-propagation) + * [CoroutineExceptionHandler](docs/exception-handling.md#coroutineexceptionhandler) + * [Cancellation and exceptions](docs/exception-handling.md#cancellation-and-exceptions) + * [Exceptions aggregation](docs/exception-handling.md#exceptions-aggregation) + * [Supervision](docs/exception-handling.md#supervision) + * [Supervision job](docs/exception-handling.md#supervision-job) + * [Supervision scope](docs/exception-handling.md#supervision-scope) + * [Exceptions in supervised coroutines](docs/exception-handling.md#exceptions-in-supervised-coroutines) * [Shared mutable state and concurrency](docs/shared-mutable-state-and-concurrency.md#shared-mutable-state-and-concurrency) * [The problem](docs/shared-mutable-state-and-concurrency.md#the-problem) @@ -72,7 +111,7 @@ The main coroutines guide has moved to the [docs folder](docs/coroutines-guide.m * [Mutual exclusion](docs/shared-mutable-state-and-concurrency.md#mutual-exclusion) * [Actors](docs/shared-mutable-state-and-concurrency.md#actors) -* [Select expression (experimental)](docs/select-expression.md#select-expression-experimental) +* [Select Expression (experimental)](docs/select-expression.md#select-expression-experimental) * [Selecting from channels](docs/select-expression.md#selecting-from-channels) * [Selecting on close](docs/select-expression.md#selecting-on-close) * [Selecting to send](docs/select-expression.md#selecting-to-send) diff --git a/docs/_nav.yml b/docs/_nav.yml index 3a7dbfc4af..79a69690ee 100644 --- a/docs/_nav.yml +++ b/docs/_nav.yml @@ -7,21 +7,24 @@ - md: cancellation-and-timeouts.md url: cancellation-and-timeouts.html title: Cancellation and Timeouts -- md: channels.md - url: channels.html - title: Channels - md: composing-suspending-functions.md url: composing-suspending-functions.html title: Composing Suspending Functions - md: coroutine-context-and-dispatchers.md url: coroutine-context-and-dispatchers.html title: Coroutine Context and Dispatchers +- md: flow.md + url: flow.html + title: Asynchronous Flow +- md: channels.md + url: channels.html + title: Channels - md: exception-handling.md url: exception-handling.html - title: Exception Handling -- md: select-expression.md - url: select-expression.html - title: Select Expression + title: Exception Handling and Supervision - md: shared-mutable-state-and-concurrency.md url: shared-mutable-state-and-concurrency.html title: Shared Mutable State and Concurrency +- md: select-expression.md + url: select-expression.html + title: Select Expression (experimental) diff --git a/docs/basics.md b/docs/basics.md index a7321bb2f1..6a1248b56d 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -20,7 +20,7 @@ class BasicsGuideTest { -* [Coroutine basics](#coroutine-basics) +* [Coroutine Basics](#coroutine-basics) * [Your first coroutine](#your-first-coroutine) * [Bridging blocking and non-blocking worlds](#bridging-blocking-and-non-blocking-worlds) * [Waiting for a job](#waiting-for-a-job) @@ -33,7 +33,7 @@ class BasicsGuideTest { -## Coroutine basics +## Coroutine Basics This section covers basic coroutine concepts. diff --git a/docs/cancellation-and-timeouts.md b/docs/cancellation-and-timeouts.md index afb8da978b..ef4a9c9e09 100644 --- a/docs/cancellation-and-timeouts.md +++ b/docs/cancellation-and-timeouts.md @@ -19,7 +19,7 @@ class CancellationTimeOutsGuideTest { -* [Cancellation and timeouts](#cancellation-and-timeouts) +* [Cancellation and Timeouts](#cancellation-and-timeouts) * [Cancelling coroutine execution](#cancelling-coroutine-execution) * [Cancellation is cooperative](#cancellation-is-cooperative) * [Making computation code cancellable](#making-computation-code-cancellable) @@ -29,7 +29,7 @@ class CancellationTimeOutsGuideTest { -## Cancellation and timeouts +## Cancellation and Timeouts This section covers coroutine cancellation and timeouts. diff --git a/docs/composing-suspending-functions.md b/docs/composing-suspending-functions.md index a910f49cc2..0cd02762ee 100644 --- a/docs/composing-suspending-functions.md +++ b/docs/composing-suspending-functions.md @@ -20,7 +20,7 @@ class ComposingGuideTest { -* [Composing suspending functions](#composing-suspending-functions) +* [Composing Suspending Functions](#composing-suspending-functions) * [Sequential by default](#sequential-by-default) * [Concurrent using async](#concurrent-using-async) * [Lazily started async](#lazily-started-async) @@ -29,7 +29,7 @@ class ComposingGuideTest { -## Composing suspending functions +## Composing Suspending Functions This section covers various approaches to composition of suspending functions. diff --git a/docs/coroutine-context-and-dispatchers.md b/docs/coroutine-context-and-dispatchers.md index 4769c1e23d..558b039744 100644 --- a/docs/coroutine-context-and-dispatchers.md +++ b/docs/coroutine-context-and-dispatchers.md @@ -20,7 +20,7 @@ class DispatchersGuideTest { -* [Coroutine context and dispatchers](#coroutine-context-and-dispatchers) +* [Coroutine Context and Dispatchers](#coroutine-context-and-dispatchers) * [Dispatchers and threads](#dispatchers-and-threads) * [Unconfined vs confined dispatcher](#unconfined-vs-confined-dispatcher) * [Debugging coroutines and threads](#debugging-coroutines-and-threads) @@ -35,7 +35,7 @@ class DispatchersGuideTest { -## Coroutine context and dispatchers +## Coroutine Context and Dispatchers Coroutines always execute in some context represented by a value of the [CoroutineContext](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines/-coroutine-context/) diff --git a/docs/coroutines-guide.md b/docs/coroutines-guide.md index 3d0ca53576..5f41d06000 100644 --- a/docs/coroutines-guide.md +++ b/docs/coroutines-guide.md @@ -15,14 +15,15 @@ In order to use coroutines as well as follow the examples in this guide, you nee ## Table of contents -* [Coroutine basics](basics.md) -* [Cancellation and timeouts](cancellation-and-timeouts.md) -* [Composing suspending functions](composing-suspending-functions.md) -* [Coroutine context and dispatchers](coroutine-context-and-dispatchers.md) -* [Exception handling and supervision](exception-handling.md) -* [Channels (experimental)](channels.md) -* [Shared mutable state and concurrency](shared-mutable-state-and-concurrency.md) -* [Select expression (experimental)](select-expression.md) +* [Basics](basics.md) +* [Cancellation and Timeouts](cancellation-and-timeouts.md) +* [Composing Suspending Functions](composing-suspending-functions.md) +* [Coroutine Context and Dispatchers](coroutine-context-and-dispatchers.md) +* [Asynchronous Flow](flow.md) +* [Channels](channels.md) +* [Exception Handling and Supervision](exception-handling.md) +* [Shared Mutable State and Concurrency](shared-mutable-state-and-concurrency.md) +* [Select Expression (experimental)](select-expression.md) ## Additional references diff --git a/docs/exception-handling.md b/docs/exception-handling.md index 349b703ef7..409acd537e 100644 --- a/docs/exception-handling.md +++ b/docs/exception-handling.md @@ -19,19 +19,19 @@ class ExceptionsGuideTest { -* [Exception handling](#exception-handling) +* [Exception Handling](#exception-handling) * [Exception propagation](#exception-propagation) * [CoroutineExceptionHandler](#coroutineexceptionhandler) * [Cancellation and exceptions](#cancellation-and-exceptions) * [Exceptions aggregation](#exceptions-aggregation) -* [Supervision](#supervision) - * [Supervision job](#supervision-job) - * [Supervision scope](#supervision-scope) - * [Exceptions in supervised coroutines](#exceptions-in-supervised-coroutines) + * [Supervision](#supervision) + * [Supervision job](#supervision-job) + * [Supervision scope](#supervision-scope) + * [Exceptions in supervised coroutines](#exceptions-in-supervised-coroutines) -## Exception handling +## Exception Handling This section covers exception handling and cancellation on exceptions. @@ -355,7 +355,7 @@ Caught original java.io.IOException ``` -## Supervision +### Supervision As we have studied before, cancellation is a bidirectional relationship propagating through the whole coroutines hierarchy. But what if unidirectional cancellation is required? @@ -367,7 +367,7 @@ but if UI component is destroyed (and its job is cancelled), then it is necessar Another example is a server process that spawns several children jobs and needs to _supervise_ their execution, tracking their failures and restarting just those children jobs that had failed. -### Supervision job +#### Supervision job For these purposes [SupervisorJob][SupervisorJob()] can be used. It is similar to a regular [Job][Job()] with the only exception that cancellation is propagated only downwards. It is easy to demonstrate with an example: @@ -421,7 +421,7 @@ Second child is cancelled because supervisor is cancelled -### Supervision scope +#### Supervision scope For *scoped* concurrency [supervisorScope] can be used instead of [coroutineScope] for the same purpose. It propagates cancellation only in one direction and cancels all children only if it has failed itself. It also waits for all children before completion @@ -469,7 +469,7 @@ Caught assertion error ``` -### Exceptions in supervised coroutines +#### Exceptions in supervised coroutines Another crucial difference between regular and supervisor jobs is exception handling. Every child should handle its exceptions by itself via exception handling mechanisms. diff --git a/docs/flow.md b/docs/flow.md new file mode 100644 index 0000000000..134d808b0d --- /dev/null +++ b/docs/flow.md @@ -0,0 +1,1855 @@ + + + + +**Table of contents** + + + +* [Asynchronous Flow](#asynchronous-flow) + * [Representing multiple values](#representing-multiple-values) + * [Sequences](#sequences) + * [Suspending functions](#suspending-functions) + * [Flows](#flows) + * [Flows are cold](#flows-are-cold) + * [Flow cancellation](#flow-cancellation) + * [Flow builders](#flow-builders) + * [Intermediate flow operators](#intermediate-flow-operators) + * [Transform operator](#transform-operator) + * [Size-limiting operators](#size-limiting-operators) + * [Terminal flow operators](#terminal-flow-operators) + * [Flows are sequential](#flows-are-sequential) + * [Flow context](#flow-context) + * [Wrong emission withContext](#wrong-emission-withcontext) + * [flowOn operator](#flowon-operator) + * [Buffering](#buffering) + * [Conflation](#conflation) + * [Processing the latest value](#processing-the-latest-value) + * [Composing multiple flows](#composing-multiple-flows) + * [Zip](#zip) + * [Combine](#combine) + * [Flattening flows](#flattening-flows) + * [flatMapConcat](#flatmapconcat) + * [flatMapMerge](#flatmapmerge) + * [flatMapLatest](#flatmaplatest) + * [Flow exceptions](#flow-exceptions) + * [Collector try and catch](#collector-try-and-catch) + * [Everything is caught](#everything-is-caught) + * [Exception transparency](#exception-transparency) + * [Transparent catch](#transparent-catch) + * [Catching declaratively](#catching-declaratively) + * [Flow completion](#flow-completion) + * [Imperative finally block](#imperative-finally-block) + * [Declarative handling](#declarative-handling) + * [Upstream exceptions only](#upstream-exceptions-only) + * [Imperative versus declarative](#imperative-versus-declarative) + * [Launching flow](#launching-flow) + + + +## Asynchronous Flow + +Suspending functions asynchronously return a single value, but how can you return +multiple asynchronously computed values? That is what Kotlin Flows are for. + +### Representing multiple values + +Multiple values can be represented in Kotlin using [collections]. +For example, we can have a function `foo()` that returns a [List] +of three numbers and print them all using [forEach]: + +
+ +```kotlin +fun foo(): List = listOf(1, 2, 3) + +fun main() { + foo().forEach { value -> println(value) } +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-01.kt). + +This code outputs: + +```text +1 +2 +3 +``` + + + +#### Sequences + +If the numbers are computed with some CPU-consuming blocking code +(each computation taking 100ms) then we can represent the numbers using a [Sequence]: + +
+ +```kotlin +fun foo(): Sequence = sequence { // sequence builder + for (i in 1..3) { + Thread.sleep(100) // pretend we are computing it + yield(i) // yield next value + } +} + +fun main() { + foo().forEach { value -> println(value) } +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-02.kt). + +This code outputs the same numbers, but it waits 100ms before printing each one. + + + +#### Suspending functions + +However, this computation blocks the main thread that is running the code. +When those values are computed by an asynchronous code we can mark function `foo` with a `suspend` modifier, +so that it can perform its work without blocking and return the result as a list: + +
+ +```kotlin +import kotlinx.coroutines.* + +//sampleStart +suspend fun foo(): List { + delay(1000) // pretend we are doing something asynchronous here + return listOf(1, 2, 3) +} + +fun main() = runBlocking { + foo().forEach { value -> println(value) } +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-03.kt). + +This code prints the numbers after waiting for a second. + + + +#### Flows + +Using `List` result type we can only return all the values at once. To represent +the stream of values that are being asynchronously computed we can use [`Flow`][Flow] type similarly +to the `Sequence` type for synchronously computed values: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +//sampleStart +fun foo(): Flow = flow { // flow builder + for (i in 1..3) { + delay(100) // pretend we are doing something useful here + emit(i) // emit next value + } +} + +fun main() = runBlocking { + // Launch a concurrent coroutine to see that the main thread is not blocked + launch { + for (k in 1..3) { + println("I'm not blocked $k") + delay(100) + } + } + // Collect the flow + foo().collect { value -> println(value) } +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-04.kt). + +This code waits 100ms before printing each number without blocking the main thread. This is verified +by printing "I'm not blocked" every 100ms from a separate coroutine that is running in the main thread: + +```text +I'm not blocked 1 +1 +I'm not blocked 2 +2 +I'm not blocked 3 +3 +``` + + + +Notice the following differences of the code with the [Flow] from the earlier examples: + +* A builder function for [Flow] type is called [flow]. +* Code inside the `flow { ... }` builder block can suspend. +* The function `foo()` is no longer marked with `suspend` modifier. +* Values are _emitted_ from the flow using [emit][FlowCollector.emit] function. +* Values are _collected_ from the flow using [collect][collect] function. + +> You can replace [delay] with `Thread.sleep` in the body of `foo`'s `flow { ... }` and see that the main +thread is blocked in this case. + +### Flows are cold + +Flows are _cold_ streams similarly to sequences — the code inside a [flow] builder does not +run until the flow is collected. This becomes clear in the following example: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +//sampleStart +fun foo(): Flow = flow { + println("Flow started") + for (i in 1..3) { + delay(100) + emit(i) + } +} + +fun main() = runBlocking { + println("Calling foo...") + val flow = foo() + println("Calling collect...") + flow.collect { value -> println(value) } + println("Calling collect again...") + flow.collect { value -> println(value) } +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-05.kt). + +Which prints: + +```text +Calling foo... +Calling collect... +Flow started +1 +2 +3 +Calling collect again... +Flow started +1 +2 +3 +``` + + + +That is a key reason why the `foo()` function (which returns a flow) is not marked with `suspend` modifier. +By itself, `foo()` returns quickly and does not wait for anything. The flow starts every time it is collected, +that is why we see that when we call `collect` again, we get "Flow started" printed again. + +### Flow cancellation + +Flow adheres to general cooperative cancellation of coroutines. However, flow infrastructure does not introduce +additional cancellation points. It is fully transparent for cancellation. As usual, flow collection can be +cancelled when the flow is suspended in a cancellable suspending function (like [delay]) and cannot be cancelled otherwise. + +The following example shows how the flow gets cancelled on timeout when running in [withTimeoutOrNull] block +and stops executing its code: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +//sampleStart +fun foo(): Flow = flow { + for (i in 1..3) { + delay(100) + println("Emitting $i") + emit(i) + } +} + +fun main() = runBlocking { + withTimeoutOrNull(250) { // Timeout after 250ms + foo().collect { value -> println(value) } + } + println("Done") +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-06.kt). + +Notice how only two numbers get emitted by the flow in `foo()` function, producing the following output: + +```text +Emitting 1 +1 +Emitting 2 +2 +Done +``` + + + +### Flow builders + +The `flow { ... }` builder from the previous examples is the most basic one. There are other builders for +convenient declaration of flows: + +* [flowOf] builder that defines a flow emitting a fixed set of values. +* Various collections and sequences can be converted to flows using `.asFlow()` extension functions. + +Thus, the example that prints numbers from 1 to 3 from a flow can be written as: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun main() = runBlocking { +//sampleStart + // Convert an integer range to a flow + (1..3).asFlow().collect { value -> println(value) } +//sampleEnd +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-07.kt). + + + +### Intermediate flow operators + +Flows can be transformed with operators similarly to collections and sequences. +Intermediate operators are applied to an upstream flow and return a downstream flow. +These operators are cold, just like flows are. A call to such an operator is not +a suspending function itself. It works quickly, returning the definition of a new transformed flow. + +The basic operators have familiar names like [map] and [filter]. +The important difference from sequences is that blocks of +code inside those operators can call suspending functions. + +For example, a flow of incoming requests can be +mapped to results with the [map] operator even when performing a request is a long-running +operation that is implemented by a suspending function: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +//sampleStart +suspend fun performRequest(request: Int): String { + delay(1000) // imitate long-running asynchronous work + return "response $request" +} + +fun main() = runBlocking { + (1..3).asFlow() // a flow of requests + .map { request -> performRequest(request) } + .collect { response -> println(response) } +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-08.kt). + +It produces the following three lines, each line appearing after a second: + +```text +response 1 +response 2 +response 3 +``` + + + +#### Transform operator + +Among the flow transformation operators, the most general one is called [transform]. It can be used to imitate +simple transformations like [map] and [filter] as well as implement more complex transformations. +Using `transform` operator, you can [emit][FlowCollector.emit] arbitrary values an arbitrary number of times. + +For example, using `transform` we can emit a string before performing a long-running asynchronous request +and follow it with a response: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +suspend fun performRequest(request: Int): String { + delay(1000) // imitate long-running asynchronous work + return "response $request" +} + +fun main() = runBlocking { +//sampleStart + (1..3).asFlow() // a flow of requests + .transform { request -> + emit("Making request $request") + emit(performRequest(request)) + } + .collect { response -> println(response) } +//sampleEnd +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-09.kt). + +The output of this code is: + +```text +Making request 1 +response 1 +Making request 2 +response 2 +Making request 3 +response 3 +``` + + + +#### Size-limiting operators + +Size-limiting intermediate operators like [take] cancel the execution of the flow when the corresponding limit +is reached. Cancellation in coroutines is always performed by throwing an exception so that all the resource-management +functions (like `try { ... } finally { ... }` blocks) operate normally in case of cancellation: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +//sampleStart +fun numbers(): Flow = flow { + try { + emit(1) + emit(2) + println("This line will not execute") + emit(3) + } finally { + println("Finally in numbers") + } +} + +fun main() = runBlocking { + numbers() + .take(2) // take only the first two + .collect { value -> println(value) } +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-10.kt). + +The output of this code clearly shows that execution of the `flow { ... }` body in `numbers()` function +had stopped after emitting the second number: + +```text +1 +2 +Finally in numbers +``` + + + +### Terminal flow operators + +Terminal operators on flows are _suspending functions_ that start a collection of the flow. +The [collect] operator is the most basic one, but there are other terminal operators for +convenience: + +* Conversion to various collections like [toList] and [toSet]. +* Operators to get the [first] value and to ensure that a flow emits a [single] value. +* Reducing a flow to a value with [reduce] and [fold]. + +For example: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun main() = runBlocking { +//sampleStart + val sum = (1..5).asFlow() + .map { it * it } // squares of numbers from 1 to 5 + .reduce { a, b -> a + b } // sum them (terminal operator) + println(sum) +//sampleEnd +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-11.kt). + +Prints a single number: + +```text +55 +``` + + + +### Flows are sequential + +Each individual collection of a flow is performed sequentially unless special operators that operate +on multiple flows are used. The collection works directly in the coroutine that calls a terminal operator. +No new coroutines are launched by default. +Each emitted value is processed by all intermediate operators from +upstream to downstream and is delivered to the terminal operator after that. + +See the following example that filters even integers and maps them to strings: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun main() = runBlocking { +//sampleStart + (1..5).asFlow() + .filter { + println("Filter $it") + it % 2 == 0 + } + .map { + println("Map $it") + "string $it" + }.collect { + println("Collect $it") + } +//sampleEnd +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-12.kt). + +Producing: + +```text +Filter 1 +Filter 2 +Map 2 +Collect string 2 +Filter 3 +Filter 4 +Map 4 +Collect string 4 +Filter 5 +``` + + + +### Flow context + +Collection of a flow always happens in the context of the calling coroutine. For example, if there is +a `foo` flow, then the following code runs in the context specified +by the author of this code, regardless of implementation details of the `foo` flow: + +
+ +```kotlin +withContext(context) { + foo.collect { value -> + println(value) // run in the specified context + } +} +``` + +
+ + + +This property of a flow is called _context preservation_. + +So, by default, code in the `flow { ... }` builder runs in the context that is provided by a collector +of the corresponding flow. For example, consider the implementation of `foo` that prints the thread +it is called on and emits three numbers: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun log(msg: String) = println("[${Thread.currentThread().name}] $msg") + +//sampleStart +fun foo(): Flow = flow { + log("Started foo flow") + for (i in 1..3) { + emit(i) + } +} + +fun main() = runBlocking { + foo().collect { value -> log("Collected $value") } +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-13.kt). + +Running this code produces: + +```text +[main @coroutine#1] Started foo flow +[main @coroutine#1] Collected 1 +[main @coroutine#1] Collected 2 +[main @coroutine#1] Collected 3 +``` + + + +Since `foo().collect` is called from the main thread, the body of `foo`'s flow is also called in the main thread. +This is a perfect default for fast-running or asynchronous code that does not care about the execution context and +does not block the caller. + +#### Wrong emission withContext + +However, the long-running CPU-consuming code might need to be executed in the context of [Dispatchers.Default] and UI-updating +code might need to be executed in the context of [Dispatchers.Main]. Usually, [withContext] is used +to change the context in code using Kotlin coroutines, but code in the `flow { ... }` builder has to honor context +preservation property and is not allowed to [emit][FlowCollector.emit] from a different context. + +Try running the following code: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +//sampleStart +fun foo(): Flow = flow { + // WRONG way to change context for CPU-consuming code in flow builder + kotlinx.coroutines.withContext(Dispatchers.Default) { + for (i in 1..3) { + Thread.sleep(100) // pretend we are computing it in CPU-consuming way + emit(i) // emit next value + } + } +} + +fun main() = runBlocking { + foo().collect { value -> println(value) } +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-14.kt). + +This code produces the following exception: + + + +> Note that we had to use a fully qualified name of [kotlinx.coroutines.withContext][withContext] function in this example to +demonstrate this exception. A short name of `withContext` would have resolved to a special stub function that +produces compilation error to prevent us from running into this problem. + +#### flowOn operator + +The exception refers to [flowOn] function that shall be used to change the context of flow emission. +The correct way of changing the context of a flow is shown in the below example, which also prints +names of the corresponding threads to show how it all works: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun log(msg: String) = println("[${Thread.currentThread().name}] $msg") + +//sampleStart +fun foo(): Flow = flow { + for (i in 1..3) { + Thread.sleep(100) // pretend we are computing it in CPU-consuming way + log("Emitting $i") + emit(i) // emit next value + } +}.flowOn(Dispatchers.Default) // RIGHT way to change context for CPU-consuming code in flow builder + +fun main() = runBlocking { + foo().collect { value -> + log("Collected $value") + } +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-15.kt). + +Notice how `flow { ... }` works in the background thread, while collection happens in the main thread: + + + +Another observation here is that [flowOn] operator had changed the default sequential nature of the flow. +Now collection happens in one coroutine ("coroutine#1") and emission happens in another coroutine +("coroutine#2") that is running in another thread concurrently with collecting coroutine. The [flowOn] operator +creates another coroutine for an upstream flow when it has to change the [CoroutineDispatcher] in its context. + +### Buffering + +Running different parts of a flow in different coroutines can be helpful from the standpoint of overall time it takes +to collect the flow, especially when long-running asynchronous operations are involved. For example, consider a case when +emission by `foo()` flow is slow, taking 100 ms to produce an element; and collector is also slow, +taking 300 ms to process an element. Let us see how long does it take to collect such a flow with three numbers: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlin.system.* + +//sampleStart +fun foo(): Flow = flow { + for (i in 1..3) { + delay(100) // pretend we are asynchronously waiting 100 ms + emit(i) // emit next value + } +} + +fun main() = runBlocking { + val time = measureTimeMillis { + foo().collect { value -> + delay(300) // pretend we are processing it for 300 ms + println(value) + } + } + println("Collected in $time ms") +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-16.kt). + +It produces something like this, the whole collection taking around 1200 ms (three numbers times 400 ms each): + +```text +1 +2 +3 +Collected in 1220 ms +``` + + + +We can use [buffer] operator on a flow to run emitting code of `foo()` concurrently with collecting code, +as opposed to running them sequentially: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlin.system.* + +fun foo(): Flow = flow { + for (i in 1..3) { + delay(100) // pretend we are asynchronously waiting 100 ms + emit(i) // emit next value + } +} + +fun main() = runBlocking { +//sampleStart + val time = measureTimeMillis { + foo() + .buffer() // buffer emissions, don't wait + .collect { value -> + delay(300) // pretend we are processing it for 300 ms + println(value) + } + } + println("Collected in $time ms") +//sampleEnd +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-17.kt). + +It produces the same numbers faster, as we have effectively created a processing pipeline, +only having to wait 100 ms for the first number and then spending only 300 ms to process +each number. This way it takes around 1000 ms to run: + +```text +1 +2 +3 +Collected in 1071 ms +``` + + + +> Note that [flowOn] operator uses the same buffering mechanism when it has to change [CoroutineDispatcher], +but here we explicitly request buffering without changing execution context. + +#### Conflation + +When flow represents partial results of some operation or operation status updates, it may not be necessary +to process each value, but only to process the most recent ones. In this case, [conflate] operator can be used to skip +intermediate values when a collector is too slow to process them. Building on the previous example: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlin.system.* + +fun foo(): Flow = flow { + for (i in 1..3) { + delay(100) // pretend we are asynchronously waiting 100 ms + emit(i) // emit next value + } +} + +fun main() = runBlocking { +//sampleStart + val time = measureTimeMillis { + foo() + .conflate() // conflate emissions, don't process each one + .collect { value -> + delay(300) // pretend we are processing it for 300 ms + println(value) + } + } + println("Collected in $time ms") +//sampleEnd +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-18.kt). + +We see that while the first number was being processed the second and the third ones were already produced, so +the second one was _conflated_ and only the most recent (the third one) was delivered to the collector: + +```text +1 +3 +Collected in 758 ms +``` + + + +#### Processing the latest value + +Conflation is one way to speed up processing when both emitter and collector are slow. It does that by dropping emitted values. +The other way is to cancel slow collector and restart it every time a new value is emitted. There is +a family of `xxxLatest` operators that perform the same essential logic of `xxx` operator, but cancel the +code in their block on a new value. Let us change the previous example from [conflate] to [collectLatest]: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlin.system.* + +fun foo(): Flow = flow { + for (i in 1..3) { + delay(100) // pretend we are asynchronously waiting 100 ms + emit(i) // emit next value + } +} + +fun main() = runBlocking { +//sampleStart + val time = measureTimeMillis { + foo() + .collectLatest { value -> // cancel & restart on the latest value + println("Collecting $value") + delay(300) // pretend we are processing it for 300 ms + println("Done $value") + } + } + println("Collected in $time ms") +//sampleEnd +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-19.kt). + +Since the body of [collectLatest] takes 300 ms, but new values are emitted every 100 ms, we see that the block +is run on every value, but completes only for the last value: + +```text +Collecting 1 +Collecting 2 +Collecting 3 +Done 3 +Collected in 741 ms +``` + + + +### Composing multiple flows + +There are several ways to compose multiple flows. + +#### Zip + +Similarly to [Sequence.zip] extension function in the Kotlin standard library, +flows have [zip] operator that combines the corresponding values of two flows: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun main() = runBlocking { +//sampleStart + val nums = (1..3).asFlow() // numbers 1..3 + val strs = flowOf("one", "two", "three") // strings + nums.zip(strs) { a, b -> "$a -> $b" } // compose a single string + .collect { println(it) } // collect and print +//sampleEnd +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-20.kt). + +This example prints: + +```text +1 -> one +2 -> two +3 -> three +``` + + + +#### Combine + +When flow represents the most recent value of some variable or operation (see also a related +section on [conflation](#conflation)) it might be needed to perform a computation that depends on +the most recent values of the corresponding flows and to recompute it whenever any of upstream +flows emit a value. The corresponding family of operators is called [combine]. + +For example, if the numbers in the previous example update every 300ms, but strings update every 400 ms, +then zipping them using [zip] operator would still produce the same result, +albeit results are going to be printed every 400 ms: + +> We use [onEach] intermediate operator in this example to delay each element and thus make the code +that emits sample flows more declarative and shorter. + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun main() = runBlocking { +//sampleStart + val nums = (1..3).asFlow().onEach { delay(300) } // numbers 1..3 every 300 ms + val strs = flowOf("one", "two", "three").onEach { delay(400) } // strings every 400 ms + val startTime = System.currentTimeMillis() // remember the start time + nums.zip(strs) { a, b -> "$a -> $b" } // compose a single string with "zip" + .collect { value -> // collect and print + println("$value at ${System.currentTimeMillis() - startTime} ms from start") + } +//sampleEnd +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-21.kt). + + + +However, using [combine] operator here instead of [zip]: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun main() = runBlocking { +//sampleStart + val nums = (1..3).asFlow().onEach { delay(300) } // numbers 1..3 every 300 ms + val strs = flowOf("one", "two", "three").onEach { delay(400) } // strings every 400 ms + val startTime = currentTimeMillis() // remember the start time + nums.combine(strs) { a, b -> "$a -> $b" } // compose a single string with "combine" + .collect { value -> // collect and print + println("$value at ${System.currentTimeMillis() - startTime} ms from start") + } +//sampleEnd +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-22.kt). + +We get quite a different output, where a line is printed at each emission from either `nums` or `strs` flows: + +```text +1 -> one at 452 ms from start +2 -> one at 651 ms from start +2 -> two at 854 ms from start +3 -> two at 952 ms from start +3 -> three at 1256 ms from start +``` + + + +### Flattening flows + +Flows represent asynchronously received sequences of values, so it is quite easy to get in a situation where +each value triggers a request for another sequence of values. For example, we can have the following +function that returns a flow of two strings 500 ms apart: + +
+ +```kotlin +fun requestFlow(i: Int): Flow = flow { + emit("$i: First") + delay(500) // wait 500 ms + emit("$i: Second") +} +``` + +
+ + + +Now if we have a flow of three integers and call `requestFlow` for each of them like this: + +
+ +```kotlin +(1..3).asFlow().map { requestFlow(it) } +``` + +
+ + + +Then we end up with a flow of flows (`Flow>`) that needs to be _flattened_ into a single flow for +further processing. Collections and sequences have [flatten][Sequence.flatten] and [flatMap][Sequence.flatMap] +operators for this purpose. However, the asynchronous nature of flows calls for different _modes_ of flattening +thus there is a family of flattening operators on flows. + +#### flatMapConcat + +Concatenating mode is implemented by [flatMapConcat] and [flattenConcat] operators. They are the most direct +analogues of the corresponding sequence operators. They wait for inner flow to complete before +starting to collect the next one as the following example shows: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun requestFlow(i: Int): Flow = flow { + emit("$i: First") + delay(500) // wait 500 ms + emit("$i: Second") +} + +fun main() = runBlocking { +//sampleStart + val startTime = currentTimeMillis() // remember the start time + (1..3).asFlow().onEach { delay(100) } // a number every 100 ms + .flatMapConcat { requestFlow(it) } + .collect { value -> // collect and print + println("$value at ${System.currentTimeMillis() - startTime} ms from start") + } +//sampleEnd +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-23.kt). + +The sequential nature of [flatMapConcat] is clearly seen in the output: + +```text +1: First at 121 ms from start +1: Second at 622 ms from start +2: First at 727 ms from start +2: Second at 1227 ms from start +3: First at 1328 ms from start +3: Second at 1829 ms from start +``` + + + +#### flatMapMerge + +Another flattening mode is to concurrently collect all the incoming flows and merge their values into +a single flow so that values are emitted as soon as possible. +It is implemented by [flatMapMerge] and [flattenMerge] operators. They both accept an optional +`concurrency` parameter that limits the number of concurrent flows that are collected at the same time +(it is equal to [DEFAULT_CONCURRENCY] by default). + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun requestFlow(i: Int): Flow = flow { + emit("$i: First") + delay(500) // wait 500 ms + emit("$i: Second") +} + +fun main() = runBlocking { +//sampleStart + val startTime = currentTimeMillis() // remember the start time + (1..3).asFlow().onEach { delay(100) } // a number every 100 ms + .flatMapMerge { requestFlow(it) } + .collect { value -> // collect and print + println("$value at ${System.currentTimeMillis() - startTime} ms from start") + } +//sampleEnd +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-24.kt). + +The concurrent nature of [flatMapMerge] is obvious: + +```text +1: First at 136 ms from start +2: First at 231 ms from start +3: First at 333 ms from start +1: Second at 639 ms from start +2: Second at 732 ms from start +3: Second at 833 ms from start +``` + + + +> Note that [flatMapMerge] call its block of code (`{ requestFlow(it) }` in this example) sequentially, but +collects the resulting flows concurrently, so it is equivalent to performing a sequential +`map { requestFlow(it) }` first and then calling [flattenMerge] on the result. + +#### flatMapLatest + +In a similar way to [collectLatest] operator that was shown in +["Processing the latest value"](#processing-the-latest-value) section, there is the corresponding "Latest" +flattening mode where collection of the previous flow is cancelled as soon as new flow is emitted. +It is implemented by [flatMapLatest] operator. + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun requestFlow(i: Int): Flow = flow { + emit("$i: First") + delay(500) // wait 500 ms + emit("$i: Second") +} + +fun main() = runBlocking { +//sampleStart + val startTime = currentTimeMillis() // remember the start time + (1..3).asFlow().onEach { delay(100) } // a number every 100 ms + .flatMapLatest { requestFlow(it) } + .collect { value -> // collect and print + println("$value at ${System.currentTimeMillis() - startTime} ms from start") + } +//sampleEnd +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-25.kt). + +The output of this example speaks for the way [flatMapLatest] works: + +```text +1: First at 142 ms from start +2: First at 322 ms from start +3: First at 425 ms from start +3: Second at 931 ms from start +``` + + + +> Note that [flatMapLatest] cancels all the code in its block (`{ requestFlow(it) }` in this example) on a new value. +It makes no difference in this particular example, because the call to `requestFlow` itself is fast, not-suspending, +and cannot be cancelled. However, it would show up if we were to use suspending functions like `delay` in there. + +### Flow exceptions + +Flow collection can complete with an exception when emitter or any code inside any of the operators throw an exception. +There are several ways to handle these exceptions. + +#### Collector try and catch + +A collector can use Kotlin's [`try/catch`][exceptions] block to handle exceptions: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +//sampleStart +fun foo(): Flow = flow { + for (i in 1..3) { + println("Emitting $i") + emit(i) // emit next value + } +} + +fun main() = runBlocking { + try { + foo().collect { value -> + println(value) + check(value <= 1) { "Collected $value" } + } + } catch (e: Throwable) { + println("Caught $e") + } +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-26.kt). + +This code successfully catches an exception in [collect] terminal operator and, +as you can see, no more values are emitted after that: + +```text +Emitting 1 +1 +Emitting 2 +2 +Caught java.lang.IllegalStateException: Collected 2 +``` + + + +#### Everything is caught + +The previous example actually catches any exception happening in emitter or in any intermediate or terminal operators. +For example, let us change the code so that emitted values are [mapped][map] to strings, +but the corresponding code produces an exception: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +//sampleStart +fun foo(): Flow = + flow { + for (i in 1..3) { + println("Emitting $i") + emit(i) // emit next value + } + } + .map { value -> + check(value <= 1) { "Crashed on $value" } + "string $value" + } + +fun main() = runBlocking { + try { + foo().collect { value -> println(value) } + } catch (e: Throwable) { + println("Caught $e") + } +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-27.kt). + +This exception is still caught and collection is stopped: + +```text +Emitting 1 +string 1 +Emitting 2 +Caught java.lang.IllegalStateException: Crashed on 2 +``` + + + +### Exception transparency + +But how can code of emitter encapsulate its exception handling behavior? + +Flows must be _transparent to exceptions_ and it is a violation of exception transparency to [emit][FlowCollector.emit] values in the +`flow { ... }` builder from inside of `try/catch` block. This guarantees that a collector throwing an exception +can always catch it using `try/catch` as in the previous example. + +The emitter can use [catch] operator that preserves this exception transparency and allows encapsulation +of its exception handling. The body of the `catch` operator can analyze an exception +and react to it in different ways depending on which exception was caught: + +* Exceptions can be rethrown using `throw`. +* Exceptions can be turned into emission of values using [emit][FlowCollector.emit] from the body of [catch]. +* Exceptions can be ignored, logged, or processed by some other code. + +For example, let us emit a text on catching an exception: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = + flow { + for (i in 1..3) { + println("Emitting $i") + emit(i) // emit next value + } + } + .map { value -> + check(value <= 1) { "Crashed on $value" } + "string $value" + } + +fun main() = runBlocking { +//sampleStart + foo() + .catch { e -> emit("Caught $e") } // emit on exception + .collect { value -> println(value) } +//sampleEnd +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-28.kt). + +The output of the example is the same, even though we do not have `try/catch` around the code anymore. + + + +#### Transparent catch + +The [catch] intermediate operator, honoring exception transparency, catches only upstream exceptions +(that is an exception from all the operators above `catch`, but not below it). +If the block in `collect { ... }` (placed below `catch`) throws an exception then it escapes: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +//sampleStart +fun foo(): Flow = flow { + for (i in 1..3) { + println("Emitting $i") + emit(i) + } +} + +fun main() = runBlocking { + foo() + .catch { e -> println("Caught $e") } // does not catch downstream exceptions + .collect { value -> + check(value <= 1) { "Collected $value" } + println(value) + } +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-29.kt). + +The "Caught ..." message is not printed despite the `catch` operator: + + + +#### Catching declaratively + +We can combine a declarative nature of [catch] operator with a desire to handle all exceptions by moving the body +of [collect] operator into [onEach] and putting it before the `catch` operator. Collection of this flow must +be triggered by a call to `collect()` without parameters: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = flow { + for (i in 1..3) { + println("Emitting $i") + emit(i) + } +} + +fun main() = runBlocking { +//sampleStart + foo() + .onEach { value -> + check(value <= 1) { "Collected $value" } + println(value) + } + .catch { e -> println("Caught $e") } + .collect() +//sampleEnd +} +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-30.kt). + +Now we can see that "Caught ..." message is printed and thus we can catch all exceptions without explicitly +using a `try/catch` block: + + + +### Flow completion + +When flow collection completes (normally or exceptionally) it may be needed to execute some action. +As you might have already noticed, it also can be done in two ways: imperative and declarative. + +#### Imperative finally block + +In addition to `try`/`catch`, a collector can also use `finally` block to execute an action +upon `collect` completion. + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +//sampleStart +fun foo(): Flow = (1..3).asFlow() + +fun main() = runBlocking { + try { + foo().collect { value -> println(value) } + } finally { + println("Done") + } +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-31.kt). + +This code prints three numbers produced by the `foo()` flow followed by "Done" string: + +```text +1 +2 +3 +Done +``` + + + +#### Declarative handling + +For declarative approach, flow has [onCompletion] intermediate operator that is invoked +when the flow is completely collected. + +The previous example can be rewritten using [onCompletion] operator and produces the same output: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = (1..3).asFlow() + +fun main() = runBlocking { +//sampleStart + foo() + .onCompletion { println("Done") } + .collect { value -> println(value) } +//sampleEnd +} +``` +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-32.kt). + + + +The key advantage of [onCompletion] is a nullable `Throwable` parameter of the lambda that can be used +to determine whether flow collection was completed normally or exceptionally. In the following +example `foo()` flow throws exception after emitting number 1: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +//sampleStart +fun foo(): Flow = flow { + emit(1) + throw RuntimeException() +} + +fun main() = runBlocking { + foo() + .onCompletion { cause -> if (cause != null) println("Flow completed exceptionally") } + .catch { cause -> println("Caught exception") } + .collect { value -> println(value) } +} +//sampleEnd +``` +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-33.kt). + +As you may expect, it prints: + +```text +1 +Flow completed exceptionally +Caught exception +``` + + + +[onCompletion] operator, unlike [catch], does not handle the exception. As we can see from the above +example code, the exception still flows downstream. It will be delivered to further `onCompletion` operators +and can be handled with `catch` operator. + +#### Upstream exceptions only + +Just like [catch] operator, [onCompletion] sees only exception coming from upstream and does not +see downstream exceptions. For example, run the following code: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +//sampleStart +fun foo(): Flow = (1..3).asFlow() + +fun main() = runBlocking { + foo() + .onCompletion { cause -> println("Flow completed with $cause") } + .collect { value -> + check(value <= 1) { "Collected $value" } + println(value) + } +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-34.kt). + +And you can see the completion cause is null, yet collection failed with exception: + +```text +1 +Flow completed with null +Exception in thread "main" java.lang.IllegalStateException: Collected 2 +``` + + + +### Imperative versus declarative + +Now we know how to collect flow, handle its completion and exceptions in both imperative and declarative ways. +The natural question here is which approach should be preferred and why. +As a library, we do not advocate for any particular approach and believe that both options +are valid and should be selected according to your own preferences and code style. + +### Launching flow + +It is convenient to use flows to represent asynchronous events that are coming from some source. +In this case, we need an analogue of `addEventListener` function that registers a piece of code with a reaction +on incoming events and continues further work. The [onEach] operator can serve this role. +However, `onEach` is an intermediate operator. We also need a terminal operator to collect the flow. +Otherwise, just calling `onEach` has no effect. + +If we use [collect] terminal operator after `onEach`, then code after it waits until the flow is collected: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +//sampleStart +// Imitate a flow of events +fun events(): Flow = (1..3).asFlow().onEach { delay(100) } + +fun main() = runBlocking { + events() + .onEach { event -> println("Event: $event") } + .collect() // <--- Collecting the flow waits + println("Done") +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-35.kt). + +As you can see, it prints: + +```text +Event: 1 +Event: 2 +Event: 3 +Done +``` + + + +Here [launchIn] terminal operator comes in handy. Replacing `collect` with `launchIn` we can +launch collection of the flow in a separate coroutine, so that execution of further code +immediately continues: + +
+ +```kotlin +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +// Imitate a flow of events +fun events(): Flow = (1..3).asFlow().onEach { delay(100) } + +//sampleStart +fun main() = runBlocking { + events() + .onEach { event -> println("Event: $event") } + .launchIn(this) // <--- Launching the flow in a separate coroutine + println("Done") +} +//sampleEnd +``` + +
+ +> You can get full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-flow-36.kt). + +It prints: + +```text +Done +Event: 1 +Event: 2 +Event: 3 +``` + + + +The required parameter to `launchIn` must specify a [CoroutineScope] in which the coroutine to collect the flow is +launched. In the above example this scope comes from [runBlocking] +coroutine builder, so while the flow is running this [runBlocking] scope waits for completion of its child coroutine +and keeps the main function from returning and terminating this example. + +In real applications a scope is going to come from some entity with a limited +lifetime. As soon as the lifetime of this entity is terminated the corresponding scope is cancelled, cancelling +collection of the corresponding flow. This way the pair of `onEach { ... }.launchIn(scope)` works +like `addEventListener`. However, there is no need for the corresponding `removeEventListener` function, +as cancellation and structured concurrency serve this purpose. + +Note, that [launchIn] also returns a [Job] which can be used to [cancel][Job.cancel] the corresponding flow collection +coroutine only without cancelling the whole scope or to [join][Job.join] it. + + + +[collections]: https://kotlinlang.org/docs/reference/collections-overview.html +[List]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html +[forEach]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/for-each.html +[Sequence]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/index.html +[Sequence.zip]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/zip.html +[Sequence.flatten]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/flatten.html +[Sequence.flatMap]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/flat-map.html +[exceptions]: https://kotlinlang.org/docs/reference/exceptions.html + + + +[delay]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html +[withTimeoutOrNull]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-timeout-or-null.html +[Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html +[Dispatchers.Main]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html +[withContext]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html +[CoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/index.html +[CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html +[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html +[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html +[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html +[Job.join]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/join.html + +[Flow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/index.html +[flow]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flow.html +[FlowCollector.emit]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow-collector/emit.html +[collect]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/collect.html +[flowOf]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flow-of.html +[map]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/map.html +[filter]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/filter.html +[transform]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/transform.html +[take]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/take.html +[toList]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/to-list.html +[toSet]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/to-set.html +[first]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/first.html +[single]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/single.html +[reduce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/reduce.html +[fold]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/fold.html +[flowOn]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flow-on.html +[buffer]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/buffer.html +[conflate]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/conflate.html +[collectLatest]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/collect-latest.html +[zip]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/zip.html +[combine]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/combine.html +[onEach]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/on-each.html +[flatMapConcat]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flat-map-concat.html +[flattenConcat]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flatten-concat.html +[flatMapMerge]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flat-map-merge.html +[flattenMerge]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flatten-merge.html +[DEFAULT_CONCURRENCY]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-d-e-f-a-u-l-t_-c-o-n-c-u-r-r-e-n-c-y.html +[flatMapLatest]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/flat-map-latest.html +[catch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/catch.html +[onCompletion]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/on-completion.html +[launchIn]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/launch-in.html + diff --git a/docs/select-expression.md b/docs/select-expression.md index 35480abf36..f36fa09b6b 100644 --- a/docs/select-expression.md +++ b/docs/select-expression.md @@ -21,7 +21,7 @@ class SelectGuideTest { -* [Select expression (experimental)](#select-expression-experimental) +* [Select Expression (experimental)](#select-expression-experimental) * [Selecting from channels](#selecting-from-channels) * [Selecting on close](#selecting-on-close) * [Selecting to send](#selecting-to-send) @@ -32,7 +32,7 @@ class SelectGuideTest { -## Select expression (experimental) +## Select Expression (experimental) Select expression makes it possible to await multiple suspending functions simultaneously and _select_ the first one that becomes available. diff --git a/knit/src/Knit.kt b/knit/src/Knit.kt index abb66dfac4..d3e0a35894 100644 --- a/knit/src/Knit.kt +++ b/knit/src/Knit.kt @@ -28,6 +28,9 @@ const val INCLUDE_DIRECTIVE = "INCLUDE" const val CLEAR_DIRECTIVE = "CLEAR" const val TEST_DIRECTIVE = "TEST" +const val KNIT_AUTONUMBER_PLACEHOLDER = '#' +const val KNIT_AUTONUMBER_REGEX = "([0-9a-z]+)" + const val TEST_OUT_DIRECTIVE = "TEST_OUT" const val MODULE_DIRECTIVE = "MODULE" @@ -36,6 +39,9 @@ const val INDEX_DIRECTIVE = "INDEX" const val CODE_START = "```kotlin" const val CODE_END = "```" +const val SAMPLE_START = "//sampleStart" +const val SAMPLE_END = "//sampleEnd" + const val TEST_START = "```text" const val TEST_END = "```" @@ -73,6 +79,9 @@ fun knit(markdownFile: File): Boolean { println("*** Reading $markdownFile") val tocLines = arrayListOf() var knitRegex: Regex? = null + var knitAutonumberGroup = 0 + var knitAutonumberDigits = 0 + var knitAutonumberIndex = 1 val includes = arrayListOf() val codeLines = arrayListOf() val testLines = arrayListOf() @@ -122,7 +131,18 @@ fun knit(markdownFile: File): Boolean { requireSingleLine(directive) require(!directive.param.isEmpty()) { "$KNIT_DIRECTIVE directive must include regex parameter" } require(knitRegex == null) { "Only one KNIT directive is supported"} - knitRegex = Regex("\\((" + directive.param + ")\\)") + var str = directive.param + val i = str.indexOf(KNIT_AUTONUMBER_PLACEHOLDER) + if (i >= 0) { + val j = str.lastIndexOf(KNIT_AUTONUMBER_PLACEHOLDER) + knitAutonumberDigits = j - i + 1 + require(str.substring(i, j + 1) == KNIT_AUTONUMBER_PLACEHOLDER.toString().repeat(knitAutonumberDigits)) { + "$KNIT_DIRECTIVE can only use a contiguous range of '$KNIT_AUTONUMBER_PLACEHOLDER' for auto-numbering" + } + knitAutonumberGroup = str.substring(0, i).count { it == '(' } + 2 // note: it does not understand escaped open braces + str = str.substring(0, i) + KNIT_AUTONUMBER_REGEX + str.substring(j + 1) + } + knitRegex = Regex("\\((" + str + ")\\)") continue@mainLoop } INCLUDE_DIRECTIVE -> { @@ -183,7 +203,9 @@ fun knit(markdownFile: File): Boolean { if (inLine.startsWith(CODE_START)) { require(testOut == null || testLines.isEmpty()) { "Previous test was not emitted with $TEST_DIRECTIVE" } codeLines += "" - readUntilTo(CODE_END, codeLines) + readUntilTo(CODE_END, codeLines) { line -> + !line.startsWith(SAMPLE_START) && !line.startsWith(SAMPLE_END) + } continue@mainLoop } if (inLine.startsWith(TEST_START)) { @@ -212,8 +234,19 @@ fun knit(markdownFile: File): Boolean { remainingApiRefNames += apiRef.name } } - knitRegex?.find(inLine)?.let { knitMatch -> + knitRegex?.find(inLine)?.let knitRegexMatch@{ knitMatch -> val fileName = knitMatch.groups[1]!!.value + if (knitAutonumberDigits != 0) { + val numGroup = knitMatch.groups[knitAutonumberGroup]!! + val num = knitAutonumberIndex.toString().padStart(knitAutonumberDigits, '0') + if (numGroup.value != num) { // update and retry with this line if a different number + val r = numGroup.range + val newLine = inLine.substring(0, r.first) + num + inLine.substring(r.last + 1) + updateLineAndRetry(newLine) + return@knitRegexMatch + } + } + knitAutonumberIndex++ val file = File(markdownFile.parentFile, fileName) require(files.add(file)) { "Duplicate file: $file"} println("Knitting $file ...") @@ -328,11 +361,11 @@ private fun flushTestOut(parentDir: File?, testOut: String?, testOutLines: Mutab private fun MarkdownTextReader.readUntil(marker: String): List = arrayListOf().also { readUntilTo(marker, it) } -private fun MarkdownTextReader.readUntilTo(marker: String, list: MutableList) { +private fun MarkdownTextReader.readUntilTo(marker: String, list: MutableList, linePredicate: (String) -> Boolean = { true }) { while (true) { val line = readLine() ?: break if (line.startsWith(marker)) break - list += line + if (linePredicate(line)) list += line } } @@ -404,6 +437,12 @@ class MarkdownTextReader(r: Reader) : LineNumberReader(r) { return line } + fun updateLineAndRetry(line: String) { + outText.removeAt(outText.lastIndex) + outText += line + putBackLine = line + } + fun replaceUntilNextDirective(lines: List): Boolean { skip = true while (true) { diff --git a/kotlinx-coroutines-core/common/src/flow/internal/SafeCollector.kt b/kotlinx-coroutines-core/common/src/flow/internal/SafeCollector.kt index 8761058e71..fec0ee96e0 100644 --- a/kotlinx-coroutines-core/common/src/flow/internal/SafeCollector.kt +++ b/kotlinx-coroutines-core/common/src/flow/internal/SafeCollector.kt @@ -77,8 +77,11 @@ internal class SafeCollector( */ if (emissionParentJob !== collectJob) { error( - "Flow invariant is violated: emission from another coroutine is detected (child of $emissionParentJob, expected child of $collectJob). " + - "FlowCollector is not thread-safe and concurrent emissions are prohibited. To mitigate this restriction please use 'channelFlow' builder instead of 'flow'" + "Flow invariant is violated:\n" + + "\t\tEmission from another coroutine is detected.\n" + + "\t\tChild of $emissionParentJob, expected child of $collectJob.\n" + + "\t\tFlowCollector is not thread-safe and concurrent emissions are prohibited.\n" + + "\t\tTo mitigate this restriction please use 'channelFlow' builder instead of 'flow'" ) } @@ -91,8 +94,10 @@ internal class SafeCollector( } if (result != collectContextSize) { error( - "Flow invariant is violated: flow was collected in $collectContext, but emission happened in $currentContext. " + - "Please refer to 'flow' documentation or use 'flowOn' instead" + "Flow invariant is violated:\n" + + "\t\tFlow was collected in $collectContext,\n" + + "\t\tbut emission happened in $currentContext.\n" + + "\t\tPlease refer to 'flow' documentation or use 'flowOn' instead" ) } } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt index e6a299efcd..a35e848196 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt @@ -8,12 +8,10 @@ package kotlinx.coroutines.guide.basic03 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart val job = GlobalScope.launch { // launch a new coroutine and keep a reference to its Job delay(1000L) println("World!") } println("Hello,") job.join() // wait until child coroutine completes -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt b/kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt index a348ef4a39..56e785fb7f 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-basic-07.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.basic07 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart GlobalScope.launch { repeat(1000) { i -> println("I'm sleeping $i ...") @@ -16,5 +15,4 @@ fun main() = runBlocking { } } delay(1300L) // just quit after delay -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-01.kt index e44b703308..ebf5171e2e 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-01.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.cancel01 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart val job = launch { repeat(1000) { i -> println("job: I'm sleeping $i ...") @@ -20,5 +19,4 @@ fun main() = runBlocking { job.cancel() // cancels the job job.join() // waits for job's completion println("main: Now I can quit.") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-02.kt index 518c0be541..e3127b41ba 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-02.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.cancel02 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart val startTime = currentTimeMillis() val job = launch(Dispatchers.Default) { var nextPrintTime = startTime @@ -25,5 +24,4 @@ fun main() = runBlocking { println("main: I'm tired of waiting!") job.cancelAndJoin() // cancels the job and waits for its completion println("main: Now I can quit.") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt index 8c1e3f83b0..d47ecd9dda 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.cancel03 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart val startTime = currentTimeMillis() val job = launch(Dispatchers.Default) { var nextPrintTime = startTime @@ -25,5 +24,4 @@ fun main() = runBlocking { println("main: I'm tired of waiting!") job.cancelAndJoin() // cancels the job and waits for its completion println("main: Now I can quit.") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt index 002521e547..45c97851aa 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.cancel04 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart val job = launch { try { repeat(1000) { i -> @@ -23,5 +22,4 @@ fun main() = runBlocking { println("main: I'm tired of waiting!") job.cancelAndJoin() // cancels the job and waits for its completion println("main: Now I can quit.") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt index 5c7debb6c5..9f2cac1c23 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.cancel05 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart val job = launch { try { repeat(1000) { i -> @@ -27,5 +26,4 @@ fun main() = runBlocking { println("main: I'm tired of waiting!") job.cancelAndJoin() // cancels the job and waits for its completion println("main: Now I can quit.") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt index 299ceb276b..f06d1187e4 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt @@ -8,12 +8,10 @@ package kotlinx.coroutines.guide.cancel06 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart withTimeout(1300L) { repeat(1000) { i -> println("I'm sleeping $i ...") delay(500L) } } -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt index 1116f91357..e2880c9129 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.cancel07 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart val result = withTimeoutOrNull(1300L) { repeat(1000) { i -> println("I'm sleeping $i ...") @@ -17,5 +16,4 @@ fun main() = runBlocking { "Done" // will get cancelled before it produces this result } println("Result is $result") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-01.kt index d3ab53b79e..36c6db316b 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-01.kt @@ -9,7 +9,6 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.* fun main() = runBlocking { -//sampleStart val channel = Channel() launch { // this might be heavy CPU-consuming computation or async logic, we'll just send five squares @@ -18,5 +17,4 @@ fun main() = runBlocking { // here we print five received integers: repeat(5) { println(channel.receive()) } println("Done!") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-02.kt index 9ab469f8dc..59f5a76807 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-02.kt @@ -9,7 +9,6 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.* fun main() = runBlocking { -//sampleStart val channel = Channel() launch { for (x in 1..5) channel.send(x * x) @@ -18,5 +17,4 @@ fun main() = runBlocking { // here we print received values using `for` loop (until the channel is closed) for (y in channel) println(y) println("Done!") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-03.kt index c6550b4d1d..5c9cfb181f 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-03.kt @@ -13,9 +13,7 @@ fun CoroutineScope.produceSquares(): ReceiveChannel = produce { } fun main() = runBlocking { -//sampleStart val squares = produceSquares() squares.consumeEach { println(it) } println("Done!") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-04.kt index 02ac7bb0f5..4eb6c37d2c 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-04.kt @@ -9,13 +9,11 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.* fun main() = runBlocking { -//sampleStart val numbers = produceNumbers() // produces integers from 1 and on val squares = square(numbers) // squares integers for (i in 1..5) println(squares.receive()) // print first five println("Done!") // we are done coroutineContext.cancelChildren() // cancel children coroutines -//sampleEnd } fun CoroutineScope.produceNumbers() = produce { diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-05.kt index 625b52c72a..8b80764af5 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-05.kt @@ -9,7 +9,6 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.* fun main() = runBlocking { -//sampleStart var cur = numbersFrom(2) for (i in 1..10) { val prime = cur.receive() @@ -17,7 +16,6 @@ fun main() = runBlocking { cur = filter(cur, prime) } coroutineContext.cancelChildren() // cancel all children to let main finish -//sampleEnd } fun CoroutineScope.numbersFrom(start: Int) = produce { diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-06.kt index b88a1b0091..452e056d34 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-06.kt @@ -9,12 +9,10 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.* fun main() = runBlocking { -//sampleStart val producer = produceNumbers() repeat(5) { launchProcessor(it, producer) } delay(950) producer.cancel() // cancel producer coroutine and thus kill them all -//sampleEnd } fun CoroutineScope.produceNumbers() = produce { diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-07.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-07.kt index 048729677b..9fc852e5c0 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-07.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-07.kt @@ -9,7 +9,6 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.* fun main() = runBlocking { -//sampleStart val channel = Channel() launch { sendString(channel, "foo", 200L) } launch { sendString(channel, "BAR!", 500L) } @@ -17,7 +16,6 @@ fun main() = runBlocking { println(channel.receive()) } coroutineContext.cancelChildren() // cancel all children to let main finish -//sampleEnd } suspend fun sendString(channel: SendChannel, s: String, time: Long) { diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-08.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-08.kt index 6c5598097c..c9916d4184 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-08.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-08.kt @@ -9,7 +9,6 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.* fun main() = runBlocking { -//sampleStart val channel = Channel(4) // create buffered channel val sender = launch { // launch sender coroutine repeat(10) { @@ -20,5 +19,4 @@ fun main() = runBlocking { // don't receive anything... just wait.... delay(1000) sender.cancel() // cancel sender coroutine -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-channel-09.kt b/kotlinx-coroutines-core/jvm/test/guide/example-channel-09.kt index ae9d95c771..fb293257e8 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-channel-09.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-channel-09.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.channel09 import kotlinx.coroutines.* import kotlinx.coroutines.channels.* -//sampleStart data class Ball(var hits: Int) fun main() = runBlocking { @@ -28,4 +27,3 @@ suspend fun player(name: String, table: Channel) { table.send(ball) // send the ball back } } -//sampleEnd diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-compose-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-compose-01.kt index 5dfb770046..ab9ef608f4 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-01.kt @@ -9,14 +9,12 @@ import kotlinx.coroutines.* import kotlin.system.* fun main() = runBlocking { -//sampleStart val time = measureTimeMillis { val one = doSomethingUsefulOne() val two = doSomethingUsefulTwo() println("The answer is ${one + two}") } println("Completed in $time ms") -//sampleEnd } suspend fun doSomethingUsefulOne(): Int { diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-compose-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-compose-02.kt index d78e5141f4..9e46c6c48f 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-02.kt @@ -9,14 +9,12 @@ import kotlinx.coroutines.* import kotlin.system.* fun main() = runBlocking { -//sampleStart val time = measureTimeMillis { val one = async { doSomethingUsefulOne() } val two = async { doSomethingUsefulTwo() } println("The answer is ${one.await() + two.await()}") } println("Completed in $time ms") -//sampleEnd } suspend fun doSomethingUsefulOne(): Int { diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-compose-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-compose-03.kt index aa6dd6f7bf..1dc2fd9bb2 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-03.kt @@ -9,7 +9,6 @@ import kotlinx.coroutines.* import kotlin.system.* fun main() = runBlocking { -//sampleStart val time = measureTimeMillis { val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() } val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() } @@ -19,7 +18,6 @@ fun main() = runBlocking { println("The answer is ${one.await() + two.await()}") } println("Completed in $time ms") -//sampleEnd } suspend fun doSomethingUsefulOne(): Int { diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt index ea6860e01a..ad0b021488 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-04.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.compose04 import kotlinx.coroutines.* import kotlin.system.* -//sampleStart // note that we don't have `runBlocking` to the right of `main` in this example fun main() { val time = measureTimeMillis { @@ -23,7 +22,6 @@ fun main() { } println("Completed in $time ms") } -//sampleEnd fun somethingUsefulOneAsync() = GlobalScope.async { doSomethingUsefulOne() diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-compose-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-compose-05.kt index e3c5403d52..e02f33e0bd 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-compose-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-compose-05.kt @@ -9,12 +9,10 @@ import kotlinx.coroutines.* import kotlin.system.* fun main() = runBlocking { -//sampleStart val time = measureTimeMillis { println("The answer is ${concurrentSum()}") } println("Completed in $time ms") -//sampleEnd } suspend fun concurrentSum(): Int = coroutineScope { diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-01.kt index 13534c7625..c3a9f5afeb 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-01.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.context01 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart launch { // context of the parent, main runBlocking coroutine println("main runBlocking : I'm working in thread ${Thread.currentThread().name}") } @@ -21,5 +20,4 @@ fun main() = runBlocking { launch(newSingleThreadContext("MyOwnThread")) { // will get its own new thread println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}") } -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-02.kt index d7be586aea..d1ec85fa9b 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-02.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.context02 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart launch(Dispatchers.Unconfined) { // not confined -- will work with main thread println("Unconfined : I'm working in thread ${Thread.currentThread().name}") delay(500) @@ -19,5 +18,4 @@ fun main() = runBlocking { delay(1000) println("main runBlocking: After delay in thread ${Thread.currentThread().name}") } -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-03.kt index a26d3a0863..e52976d095 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-03.kt @@ -10,7 +10,6 @@ import kotlinx.coroutines.* fun log(msg: String) = println("[${Thread.currentThread().name}] $msg") fun main() = runBlocking { -//sampleStart val a = async { log("I'm computing a piece of the answer") 6 @@ -20,5 +19,4 @@ fun main() = runBlocking { 7 } log("The answer is ${a.await() * b.await()}") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-04.kt index 55cfecc25b..b4a8a3f821 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-04.kt @@ -10,7 +10,6 @@ import kotlinx.coroutines.* fun log(msg: String) = println("[${Thread.currentThread().name}] $msg") fun main() { -//sampleStart newSingleThreadContext("Ctx1").use { ctx1 -> newSingleThreadContext("Ctx2").use { ctx2 -> runBlocking(ctx1) { @@ -22,5 +21,4 @@ fun main() { } } } -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-05.kt index d014f563a6..338e3c9d88 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-05.kt @@ -8,7 +8,5 @@ package kotlinx.coroutines.guide.context05 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart println("My job is ${coroutineContext[Job]}") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt index 563d418c1b..b37b06b85c 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-06.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.context06 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart // launch a coroutine to process some kind of incoming request val request = launch { // it spawns two other jobs, one with GlobalScope @@ -29,5 +28,4 @@ fun main() = runBlocking { request.cancel() // cancel processing of the request delay(1000) // delay a second to see what happens println("main: Who has survived request cancellation?") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-07.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-07.kt index 27bff49b2b..825f572a34 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-07.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-07.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.context07 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart // launch a coroutine to process some kind of incoming request val request = launch { repeat(3) { i -> // launch a few children jobs @@ -21,5 +20,4 @@ fun main() = runBlocking { } request.join() // wait for completion of the request, including all its children println("Now processing of the request is complete") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-08.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-08.kt index 2a278d29d2..1083d77da4 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-08.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-08.kt @@ -10,7 +10,6 @@ import kotlinx.coroutines.* fun log(msg: String) = println("[${Thread.currentThread().name}] $msg") fun main() = runBlocking(CoroutineName("main")) { -//sampleStart log("Started main coroutine") // run two background value computations val v1 = async(CoroutineName("v1coroutine")) { @@ -24,5 +23,4 @@ fun main() = runBlocking(CoroutineName("main")) { 6 } log("The answer for v1 / v2 = ${v1.await() / v2.await()}") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-09.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-09.kt index b72041b3ae..386e52544f 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-09.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-09.kt @@ -8,9 +8,7 @@ package kotlinx.coroutines.guide.context09 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart launch(Dispatchers.Default + CoroutineName("test")) { println("I'm working in thread ${Thread.currentThread().name}") } -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-10.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-10.kt index bce5f14353..3dde44670e 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-10.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-10.kt @@ -28,7 +28,6 @@ class Activity : CoroutineScope by CoroutineScope(Dispatchers.Default) { } // class Activity ends fun main() = runBlocking { -//sampleStart val activity = Activity() activity.doSomething() // run test function println("Launched coroutines") @@ -36,5 +35,4 @@ fun main() = runBlocking { println("Destroying activity!") activity.destroy() // cancels all coroutines delay(1000) // visually confirm that they don't work -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-context-11.kt b/kotlinx-coroutines-core/jvm/test/guide/example-context-11.kt index a26d8c6a10..4a50d86c0f 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-context-11.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-context-11.kt @@ -10,7 +10,6 @@ import kotlinx.coroutines.* val threadLocal = ThreadLocal() // declare thread-local variable fun main() = runBlocking { -//sampleStart threadLocal.set("main") println("Pre-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'") val job = launch(Dispatchers.Default + threadLocal.asContextElement(value = "launch")) { @@ -20,5 +19,4 @@ fun main() = runBlocking { } job.join() println("Post-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt index 80da1dfffb..818ab285c8 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.exceptions02 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart val handler = CoroutineExceptionHandler { _, exception -> println("Caught $exception") } @@ -19,5 +18,4 @@ fun main() = runBlocking { throw ArithmeticException() // Nothing will be printed, relying on user to call deferred.await() } joinAll(job, deferred) -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-03.kt index eadbb9bf01..2b1e8e62b1 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-03.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.exceptions03 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart val job = launch { val child = launch { try { @@ -25,5 +24,4 @@ fun main() = runBlocking { println("Parent is not cancelled") } job.join() -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt index e741d39ea9..02024ce206 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt @@ -8,7 +8,6 @@ package kotlinx.coroutines.guide.exceptions04 import kotlinx.coroutines.* fun main() = runBlocking { -//sampleStart val handler = CoroutineExceptionHandler { _, exception -> println("Caught $exception") } @@ -31,5 +30,4 @@ fun main() = runBlocking { } } job.join() -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt index 0faf8d4f08..636c4a1f5d 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt @@ -9,7 +9,6 @@ import kotlinx.coroutines.* import java.io.* fun main() = runBlocking { -//sampleStart val handler = CoroutineExceptionHandler { _, exception -> println("Caught original $exception") } @@ -29,5 +28,4 @@ fun main() = runBlocking { } } job.join() -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-01.kt new file mode 100644 index 0000000000..020f458b44 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-01.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow01 + +fun foo(): List = listOf(1, 2, 3) + +fun main() { + foo().forEach { value -> println(value) } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-02.kt new file mode 100644 index 0000000000..66fc1639b5 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-02.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow02 + +fun foo(): Sequence = sequence { // sequence builder + for (i in 1..3) { + Thread.sleep(100) // pretend we are computing it + yield(i) // yield next value + } +} + +fun main() { + foo().forEach { value -> println(value) } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-03.kt new file mode 100644 index 0000000000..393a0fa3a0 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-03.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow03 + +import kotlinx.coroutines.* + +suspend fun foo(): List { + delay(1000) // pretend we are doing something asynchronous here + return listOf(1, 2, 3) +} + +fun main() = runBlocking { + foo().forEach { value -> println(value) } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-04.kt new file mode 100644 index 0000000000..9a3c05cd2c --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-04.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow04 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = flow { // flow builder + for (i in 1..3) { + delay(100) // pretend we are doing something useful here + emit(i) // emit next value + } +} + +fun main() = runBlocking { + // Launch a concurrent coroutine to see that the main thread is not blocked + launch { + for (k in 1..3) { + println("I'm not blocked $k") + delay(100) + } + } + // Collect the flow + foo().collect { value -> println(value) } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-05.kt new file mode 100644 index 0000000000..c1e05e2e3c --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-05.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow05 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = flow { + println("Flow started") + for (i in 1..3) { + delay(100) + emit(i) + } +} + +fun main() = runBlocking { + println("Calling foo...") + val flow = foo() + println("Calling collect...") + flow.collect { value -> println(value) } + println("Calling collect again...") + flow.collect { value -> println(value) } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-06.kt new file mode 100644 index 0000000000..1926983d67 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-06.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow06 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = flow { + for (i in 1..3) { + delay(100) + println("Emitting $i") + emit(i) + } +} + +fun main() = runBlocking { + withTimeoutOrNull(250) { // Timeout after 250ms + foo().collect { value -> println(value) } + } + println("Done") +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-07.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-07.kt new file mode 100644 index 0000000000..47ecf20bae --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-07.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow07 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun main() = runBlocking { + // Convert an integer range to a flow + (1..3).asFlow().collect { value -> println(value) } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-08.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-08.kt new file mode 100644 index 0000000000..96aa19c1d5 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-08.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow08 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +suspend fun performRequest(request: Int): String { + delay(1000) // imitate long-running asynchronous work + return "response $request" +} + +fun main() = runBlocking { + (1..3).asFlow() // a flow of requests + .map { request -> performRequest(request) } + .collect { response -> println(response) } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-09.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-09.kt new file mode 100644 index 0000000000..4af29d93d3 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-09.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow09 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +suspend fun performRequest(request: Int): String { + delay(1000) // imitate long-running asynchronous work + return "response $request" +} + +fun main() = runBlocking { + (1..3).asFlow() // a flow of requests + .transform { request -> + emit("Making request $request") + emit(performRequest(request)) + } + .collect { response -> println(response) } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-10.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-10.kt new file mode 100644 index 0000000000..47602dab09 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-10.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow10 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun numbers(): Flow = flow { + try { + emit(1) + emit(2) + println("This line will not execute") + emit(3) + } finally { + println("Finally in numbers") + } +} + +fun main() = runBlocking { + numbers() + .take(2) // take only the first two + .collect { value -> println(value) } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-11.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-11.kt new file mode 100644 index 0000000000..a97400626c --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-11.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow11 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun main() = runBlocking { + val sum = (1..5).asFlow() + .map { it * it } // squares of numbers from 1 to 5 + .reduce { a, b -> a + b } // sum them (terminal operator) + println(sum) +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-12.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-12.kt new file mode 100644 index 0000000000..24dc4267ad --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-12.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow12 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun main() = runBlocking { + (1..5).asFlow() + .filter { + println("Filter $it") + it % 2 == 0 + } + .map { + println("Map $it") + "string $it" + }.collect { + println("Collect $it") + } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-13.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-13.kt new file mode 100644 index 0000000000..5d45946cb9 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-13.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow13 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun log(msg: String) = println("[${Thread.currentThread().name}] $msg") + +fun foo(): Flow = flow { + log("Started foo flow") + for (i in 1..3) { + emit(i) + } +} + +fun main() = runBlocking { + foo().collect { value -> log("Collected $value") } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-14.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-14.kt new file mode 100644 index 0000000000..6996abcd83 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-14.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow14 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = flow { + // WRONG way to change context for CPU-consuming code in flow builder + kotlinx.coroutines.withContext(Dispatchers.Default) { + for (i in 1..3) { + Thread.sleep(100) // pretend we are computing it in CPU-consuming way + emit(i) // emit next value + } + } +} + +fun main() = runBlocking { + foo().collect { value -> println(value) } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-15.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-15.kt new file mode 100644 index 0000000000..3c1b10a94b --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-15.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow15 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun log(msg: String) = println("[${Thread.currentThread().name}] $msg") + +fun foo(): Flow = flow { + for (i in 1..3) { + Thread.sleep(100) // pretend we are computing it in CPU-consuming way + log("Emitting $i") + emit(i) // emit next value + } +}.flowOn(Dispatchers.Default) // RIGHT way to change context for CPU-consuming code in flow builder + +fun main() = runBlocking { + foo().collect { value -> + log("Collected $value") + } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-16.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-16.kt new file mode 100644 index 0000000000..0698e1bf84 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-16.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow16 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlin.system.* + +fun foo(): Flow = flow { + for (i in 1..3) { + delay(100) // pretend we are asynchronously waiting 100 ms + emit(i) // emit next value + } +} + +fun main() = runBlocking { + val time = measureTimeMillis { + foo().collect { value -> + delay(300) // pretend we are processing it for 300 ms + println(value) + } + } + println("Collected in $time ms") +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-17.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-17.kt new file mode 100644 index 0000000000..86de59a2d0 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-17.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow17 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlin.system.* + +fun foo(): Flow = flow { + for (i in 1..3) { + delay(100) // pretend we are asynchronously waiting 100 ms + emit(i) // emit next value + } +} + +fun main() = runBlocking { + val time = measureTimeMillis { + foo() + .buffer() // buffer emissions, don't wait + .collect { value -> + delay(300) // pretend we are processing it for 300 ms + println(value) + } + } + println("Collected in $time ms") +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-18.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-18.kt new file mode 100644 index 0000000000..597ff78326 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-18.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow18 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlin.system.* + +fun foo(): Flow = flow { + for (i in 1..3) { + delay(100) // pretend we are asynchronously waiting 100 ms + emit(i) // emit next value + } +} + +fun main() = runBlocking { + val time = measureTimeMillis { + foo() + .conflate() // conflate emissions, don't process each one + .collect { value -> + delay(300) // pretend we are processing it for 300 ms + println(value) + } + } + println("Collected in $time ms") +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-19.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-19.kt new file mode 100644 index 0000000000..eff3d8c054 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-19.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow19 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlin.system.* + +fun foo(): Flow = flow { + for (i in 1..3) { + delay(100) // pretend we are asynchronously waiting 100 ms + emit(i) // emit next value + } +} + +fun main() = runBlocking { + val time = measureTimeMillis { + foo() + .collectLatest { value -> // cancel & restart on the latest value + println("Collecting $value") + delay(300) // pretend we are processing it for 300 ms + println("Done $value") + } + } + println("Collected in $time ms") +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-20.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-20.kt new file mode 100644 index 0000000000..0cc3df45ab --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-20.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow20 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun main() = runBlocking { + val nums = (1..3).asFlow() // numbers 1..3 + val strs = flowOf("one", "two", "three") // strings + nums.zip(strs) { a, b -> "$a -> $b" } // compose a single string + .collect { println(it) } // collect and print +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-21.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-21.kt new file mode 100644 index 0000000000..5bf0e87074 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-21.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow21 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun main() = runBlocking { + val nums = (1..3).asFlow().onEach { delay(300) } // numbers 1..3 every 300 ms + val strs = flowOf("one", "two", "three").onEach { delay(400) } // strings every 400 ms + val startTime = currentTimeMillis() // remember the start time + nums.zip(strs) { a, b -> "$a -> $b" } // compose a single string with "zip" + .collect { value -> // collect and print + println("$value at ${currentTimeMillis() - startTime} ms from start") + } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-22.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-22.kt new file mode 100644 index 0000000000..cd8f8b0c5c --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-22.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow22 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun main() = runBlocking { + val nums = (1..3).asFlow().onEach { delay(300) } // numbers 1..3 every 300 ms + val strs = flowOf("one", "two", "three").onEach { delay(400) } // strings every 400 ms + val startTime = currentTimeMillis() // remember the start time + nums.combine(strs) { a, b -> "$a -> $b" } // compose a single string with "combine" + .collect { value -> // collect and print + println("$value at ${currentTimeMillis() - startTime} ms from start") + } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-23.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-23.kt new file mode 100644 index 0000000000..742452e414 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-23.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow23 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun requestFlow(i: Int): Flow = flow { + emit("$i: First") + delay(500) // wait 500 ms + emit("$i: Second") +} + +fun main() = runBlocking { + val startTime = currentTimeMillis() // remember the start time + (1..3).asFlow().onEach { delay(100) } // a number every 100 ms + .flatMapConcat { requestFlow(it) } + .collect { value -> // collect and print + println("$value at ${currentTimeMillis() - startTime} ms from start") + } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-24.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-24.kt new file mode 100644 index 0000000000..32047a93c4 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-24.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow24 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun requestFlow(i: Int): Flow = flow { + emit("$i: First") + delay(500) // wait 500 ms + emit("$i: Second") +} + +fun main() = runBlocking { + val startTime = currentTimeMillis() // remember the start time + (1..3).asFlow().onEach { delay(100) } // a number every 100 ms + .flatMapMerge { requestFlow(it) } + .collect { value -> // collect and print + println("$value at ${currentTimeMillis() - startTime} ms from start") + } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-25.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-25.kt new file mode 100644 index 0000000000..094553095a --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-25.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow25 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun requestFlow(i: Int): Flow = flow { + emit("$i: First") + delay(500) // wait 500 ms + emit("$i: Second") +} + +fun main() = runBlocking { + val startTime = currentTimeMillis() // remember the start time + (1..3).asFlow().onEach { delay(100) } // a number every 100 ms + .flatMapLatest { requestFlow(it) } + .collect { value -> // collect and print + println("$value at ${currentTimeMillis() - startTime} ms from start") + } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-26.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-26.kt new file mode 100644 index 0000000000..037e253094 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-26.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow26 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = flow { + for (i in 1..3) { + println("Emitting $i") + emit(i) // emit next value + } +} + +fun main() = runBlocking { + try { + foo().collect { value -> + println(value) + check(value <= 1) { "Collected $value" } + } + } catch (e: Throwable) { + println("Caught $e") + } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-27.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-27.kt new file mode 100644 index 0000000000..6117cf5c62 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-27.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow27 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = + flow { + for (i in 1..3) { + println("Emitting $i") + emit(i) // emit next value + } + } + .map { value -> + check(value <= 1) { "Crashed on $value" } + "string $value" + } + +fun main() = runBlocking { + try { + foo().collect { value -> println(value) } + } catch (e: Throwable) { + println("Caught $e") + } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-28.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-28.kt new file mode 100644 index 0000000000..15acb79614 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-28.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow28 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = + flow { + for (i in 1..3) { + println("Emitting $i") + emit(i) // emit next value + } + } + .map { value -> + check(value <= 1) { "Crashed on $value" } + "string $value" + } + +fun main() = runBlocking { + foo() + .catch { e -> emit("Caught $e") } // emit on exception + .collect { value -> println(value) } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-29.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-29.kt new file mode 100644 index 0000000000..c0497df73c --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-29.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow29 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = flow { + for (i in 1..3) { + println("Emitting $i") + emit(i) + } +} + +fun main() = runBlocking { + foo() + .catch { e -> println("Caught $e") } // does not catch downstream exceptions + .collect { value -> + check(value <= 1) { "Collected $value" } + println(value) + } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-30.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-30.kt new file mode 100644 index 0000000000..5035efe23c --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-30.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow30 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = flow { + for (i in 1..3) { + println("Emitting $i") + emit(i) + } +} + +fun main() = runBlocking { + foo() + .onEach { value -> + check(value <= 1) { "Collected $value" } + println(value) + } + .catch { e -> println("Caught $e") } + .collect() +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-31.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-31.kt new file mode 100644 index 0000000000..dfa43db53e --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-31.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow31 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = (1..3).asFlow() + +fun main() = runBlocking { + try { + foo().collect { value -> println(value) } + } finally { + println("Done") + } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-32.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-32.kt new file mode 100644 index 0000000000..f541ab57d9 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-32.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow32 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = (1..3).asFlow() + +fun main() = runBlocking { + foo() + .onCompletion { println("Done") } + .collect { value -> println(value) } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-33.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-33.kt new file mode 100644 index 0000000000..1e291412c3 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-33.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow33 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = flow { + emit(1) + throw RuntimeException() +} + +fun main() = runBlocking { + foo() + .onCompletion { cause -> if (cause != null) println("Flow completed exceptionally") } + .catch { cause -> println("Caught exception") } + .collect { value -> println(value) } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-34.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-34.kt new file mode 100644 index 0000000000..df2cad2025 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-34.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow34 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +fun foo(): Flow = (1..3).asFlow() + +fun main() = runBlocking { + foo() + .onCompletion { cause -> println("Flow completed with $cause") } + .collect { value -> + check(value <= 1) { "Collected $value" } + println(value) + } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-35.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-35.kt new file mode 100644 index 0000000000..a7c6bd2100 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-35.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow35 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +// Imitate a flow of events +fun events(): Flow = (1..3).asFlow().onEach { delay(100) } + +fun main() = runBlocking { + events() + .onEach { event -> println("Event: $event") } + .collect() // <--- Collecting the flow waits + println("Done") +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-flow-36.kt b/kotlinx-coroutines-core/jvm/test/guide/example-flow-36.kt new file mode 100644 index 0000000000..9c5a57bf8f --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/example-flow-36.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +// This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.flow36 + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* + +// Imitate a flow of events +fun events(): Flow = (1..3).asFlow().onEach { delay(100) } + +fun main() = runBlocking { + events() + .onEach { event -> println("Event: $event") } + .launchIn(this) // <--- Launching the flow in a separate coroutine + println("Done") +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-select-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-select-01.kt index 24beb566f5..1939e72089 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-select-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-01.kt @@ -35,12 +35,10 @@ suspend fun selectFizzBuzz(fizz: ReceiveChannel, buzz: ReceiveChannel { -//sampleStart val fizz = fizz() val buzz = buzz() repeat(7) { selectFizzBuzz(fizz, buzz) } coroutineContext.cancelChildren() // cancel fizz & buzz coroutines -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt index c763f81b4c..0e51015105 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-02.kt @@ -26,7 +26,6 @@ suspend fun selectAorB(a: ReceiveChannel, b: ReceiveChannel): St } fun main() = runBlocking { -//sampleStart val a = produce { repeat(4) { send("Hello $it") } } @@ -37,5 +36,4 @@ fun main() = runBlocking { println(selectAorB(a, b)) } coroutineContext.cancelChildren() -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-select-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-select-03.kt index 1837aae62d..42422378f8 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-select-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-03.kt @@ -20,7 +20,6 @@ fun CoroutineScope.produceNumbers(side: SendChannel) = produce { } fun main() = runBlocking { -//sampleStart val side = Channel() // allocate side channel launch { // this is a very fast consumer for the side channel side.consumeEach { println("Side channel has $it") } @@ -31,5 +30,4 @@ fun main() = runBlocking { } println("Done consuming") coroutineContext.cancelChildren() -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-select-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-select-04.kt index 1c7818d355..2db5170206 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-select-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-04.kt @@ -20,7 +20,6 @@ fun CoroutineScope.asyncStringsList(): List> { } fun main() = runBlocking { -//sampleStart val list = asyncStringsList() val result = select { list.withIndex().forEach { (index, deferred) -> @@ -32,5 +31,4 @@ fun main() = runBlocking { println(result) val countActive = list.count { it.isActive } println("$countActive coroutines are still active") -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt index 6cda3821f8..e03be9da75 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-select-05.kt @@ -36,7 +36,6 @@ fun CoroutineScope.asyncString(str: String, time: Long) = async { } fun main() = runBlocking { -//sampleStart val chan = Channel>() // the channel for test launch { // launch printing coroutine for (s in switchMapDeferreds(chan)) @@ -52,5 +51,4 @@ fun main() = runBlocking { delay(1000) // give it time to process chan.close() // close the channel ... delay(500) // and wait some time to let it finish -//sampleEnd } diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-sync-01.kt b/kotlinx-coroutines-core/jvm/test/guide/example-sync-01.kt index e0aeeb30d0..bd710a49cd 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-01.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-01.kt @@ -23,7 +23,6 @@ suspend fun massiveRun(action: suspend () -> Unit) { println("Completed ${n * k} actions in $time ms") } -//sampleStart var counter = 0 fun main() = runBlocking { @@ -34,4 +33,3 @@ fun main() = runBlocking { } println("Counter = $counter") } -//sampleEnd diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-sync-02.kt b/kotlinx-coroutines-core/jvm/test/guide/example-sync-02.kt index 84376f7fe4..813123723a 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-02.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-02.kt @@ -23,7 +23,6 @@ suspend fun massiveRun(action: suspend () -> Unit) { println("Completed ${n * k} actions in $time ms") } -//sampleStart @Volatile // in Kotlin `volatile` is an annotation var counter = 0 @@ -35,4 +34,3 @@ fun main() = runBlocking { } println("Counter = $counter") } -//sampleEnd diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-sync-03.kt b/kotlinx-coroutines-core/jvm/test/guide/example-sync-03.kt index a2e503f6ce..1baf849acd 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-03.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-03.kt @@ -24,7 +24,6 @@ suspend fun massiveRun(action: suspend () -> Unit) { println("Completed ${n * k} actions in $time ms") } -//sampleStart var counter = AtomicInteger() fun main() = runBlocking { @@ -35,4 +34,3 @@ fun main() = runBlocking { } println("Counter = $counter") } -//sampleEnd diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-sync-04.kt b/kotlinx-coroutines-core/jvm/test/guide/example-sync-04.kt index cdf1f3c83f..e16a8113f0 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-04.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-04.kt @@ -23,7 +23,6 @@ suspend fun massiveRun(action: suspend () -> Unit) { println("Completed ${n * k} actions in $time ms") } -//sampleStart val counterContext = newSingleThreadContext("CounterContext") var counter = 0 @@ -38,4 +37,3 @@ fun main() = runBlocking { } println("Counter = $counter") } -//sampleEnd diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-sync-05.kt b/kotlinx-coroutines-core/jvm/test/guide/example-sync-05.kt index 72dd1f7814..d022961b31 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-05.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-05.kt @@ -23,7 +23,6 @@ suspend fun massiveRun(action: suspend () -> Unit) { println("Completed ${n * k} actions in $time ms") } -//sampleStart val counterContext = newSingleThreadContext("CounterContext") var counter = 0 @@ -36,4 +35,3 @@ fun main() = runBlocking { } println("Counter = $counter") } -//sampleEnd diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-sync-06.kt b/kotlinx-coroutines-core/jvm/test/guide/example-sync-06.kt index 7a401e997c..fe08f049fd 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-06.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-06.kt @@ -24,7 +24,6 @@ suspend fun massiveRun(action: suspend () -> Unit) { println("Completed ${n * k} actions in $time ms") } -//sampleStart val mutex = Mutex() var counter = 0 @@ -39,4 +38,3 @@ fun main() = runBlocking { } println("Counter = $counter") } -//sampleEnd diff --git a/kotlinx-coroutines-core/jvm/test/guide/example-sync-07.kt b/kotlinx-coroutines-core/jvm/test/guide/example-sync-07.kt index 0ef8a2ab03..49f0701756 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/example-sync-07.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/example-sync-07.kt @@ -40,7 +40,6 @@ fun CoroutineScope.counterActor() = actor { } } -//sampleStart fun main() = runBlocking { val counter = counterActor() // create the actor withContext(Dispatchers.Default) { @@ -54,4 +53,3 @@ fun main() = runBlocking { println("Counter = ${response.await()}") counter.close() // shutdown the actor } -//sampleEnd diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/FlowGuideTest.kt b/kotlinx-coroutines-core/jvm/test/guide/test/FlowGuideTest.kt new file mode 100644 index 0000000000..0353c54e7c --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/guide/test/FlowGuideTest.kt @@ -0,0 +1,381 @@ +// This file was automatically generated from flow.md by Knit tool. Do not edit. +package kotlinx.coroutines.guide.test + +import org.junit.Test + +class FlowGuideTest { + + @Test + fun testKotlinxCoroutinesGuideFlow01() { + test("KotlinxCoroutinesGuideFlow01") { kotlinx.coroutines.guide.flow01.main() }.verifyLines( + "1", + "2", + "3" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow02() { + test("KotlinxCoroutinesGuideFlow02") { kotlinx.coroutines.guide.flow02.main() }.verifyLines( + "1", + "2", + "3" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow03() { + test("KotlinxCoroutinesGuideFlow03") { kotlinx.coroutines.guide.flow03.main() }.verifyLines( + "1", + "2", + "3" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow04() { + test("KotlinxCoroutinesGuideFlow04") { kotlinx.coroutines.guide.flow04.main() }.verifyLines( + "I'm not blocked 1", + "1", + "I'm not blocked 2", + "2", + "I'm not blocked 3", + "3" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow05() { + test("KotlinxCoroutinesGuideFlow05") { kotlinx.coroutines.guide.flow05.main() }.verifyLines( + "Calling foo...", + "Calling collect...", + "Flow started", + "1", + "2", + "3", + "Calling collect again...", + "Flow started", + "1", + "2", + "3" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow06() { + test("KotlinxCoroutinesGuideFlow06") { kotlinx.coroutines.guide.flow06.main() }.verifyLines( + "Emitting 1", + "1", + "Emitting 2", + "2", + "Done" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow07() { + test("KotlinxCoroutinesGuideFlow07") { kotlinx.coroutines.guide.flow07.main() }.verifyLines( + "1", + "2", + "3" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow08() { + test("KotlinxCoroutinesGuideFlow08") { kotlinx.coroutines.guide.flow08.main() }.verifyLines( + "response 1", + "response 2", + "response 3" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow09() { + test("KotlinxCoroutinesGuideFlow09") { kotlinx.coroutines.guide.flow09.main() }.verifyLines( + "Making request 1", + "response 1", + "Making request 2", + "response 2", + "Making request 3", + "response 3" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow10() { + test("KotlinxCoroutinesGuideFlow10") { kotlinx.coroutines.guide.flow10.main() }.verifyLines( + "1", + "2", + "Finally in numbers" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow11() { + test("KotlinxCoroutinesGuideFlow11") { kotlinx.coroutines.guide.flow11.main() }.verifyLines( + "55" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow12() { + test("KotlinxCoroutinesGuideFlow12") { kotlinx.coroutines.guide.flow12.main() }.verifyLines( + "Filter 1", + "Filter 2", + "Map 2", + "Collect string 2", + "Filter 3", + "Filter 4", + "Map 4", + "Collect string 4", + "Filter 5" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow13() { + test("KotlinxCoroutinesGuideFlow13") { kotlinx.coroutines.guide.flow13.main() }.verifyLinesFlexibleThread( + "[main @coroutine#1] Started foo flow", + "[main @coroutine#1] Collected 1", + "[main @coroutine#1] Collected 2", + "[main @coroutine#1] Collected 3" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow14() { + test("KotlinxCoroutinesGuideFlow14") { kotlinx.coroutines.guide.flow14.main() }.verifyExceptions( + "Exception in thread \"main\" java.lang.IllegalStateException: Flow invariant is violated:", + " Flow was collected in [CoroutineId(1), \"coroutine#1\":BlockingCoroutine{Active}@5511c7f8, BlockingEventLoop@2eac3323],", + " but emission happened in [CoroutineId(1), \"coroutine#1\":DispatchedCoroutine{Active}@2dae0000, DefaultDispatcher].", + " Please refer to 'flow' documentation or use 'flowOn' instead", + " at ..." + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow15() { + test("KotlinxCoroutinesGuideFlow15") { kotlinx.coroutines.guide.flow15.main() }.verifyLinesFlexibleThread( + "[DefaultDispatcher-worker-1 @coroutine#2] Emitting 1", + "[main @coroutine#1] Collected 1", + "[DefaultDispatcher-worker-1 @coroutine#2] Emitting 2", + "[main @coroutine#1] Collected 2", + "[DefaultDispatcher-worker-1 @coroutine#2] Emitting 3", + "[main @coroutine#1] Collected 3" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow16() { + test("KotlinxCoroutinesGuideFlow16") { kotlinx.coroutines.guide.flow16.main() }.verifyLinesArbitraryTime( + "1", + "2", + "3", + "Collected in 1220 ms" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow17() { + test("KotlinxCoroutinesGuideFlow17") { kotlinx.coroutines.guide.flow17.main() }.verifyLinesArbitraryTime( + "1", + "2", + "3", + "Collected in 1071 ms" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow18() { + test("KotlinxCoroutinesGuideFlow18") { kotlinx.coroutines.guide.flow18.main() }.verifyLinesArbitraryTime( + "1", + "3", + "Collected in 758 ms" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow19() { + test("KotlinxCoroutinesGuideFlow19") { kotlinx.coroutines.guide.flow19.main() }.verifyLinesArbitraryTime( + "Collecting 1", + "Collecting 2", + "Collecting 3", + "Done 3", + "Collected in 741 ms" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow20() { + test("KotlinxCoroutinesGuideFlow20") { kotlinx.coroutines.guide.flow20.main() }.verifyLines( + "1 -> one", + "2 -> two", + "3 -> three" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow21() { + test("KotlinxCoroutinesGuideFlow21") { kotlinx.coroutines.guide.flow21.main() }.verifyLinesArbitraryTime( + "1 -> one at 437 ms from start", + "2 -> two at 837 ms from start", + "3 -> three at 1243 ms from start" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow22() { + test("KotlinxCoroutinesGuideFlow22") { kotlinx.coroutines.guide.flow22.main() }.verifyLinesArbitraryTime( + "1 -> one at 452 ms from start", + "2 -> one at 651 ms from start", + "2 -> two at 854 ms from start", + "3 -> two at 952 ms from start", + "3 -> three at 1256 ms from start" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow23() { + test("KotlinxCoroutinesGuideFlow23") { kotlinx.coroutines.guide.flow23.main() }.verifyLinesArbitraryTime( + "1: First at 121 ms from start", + "1: Second at 622 ms from start", + "2: First at 727 ms from start", + "2: Second at 1227 ms from start", + "3: First at 1328 ms from start", + "3: Second at 1829 ms from start" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow24() { + test("KotlinxCoroutinesGuideFlow24") { kotlinx.coroutines.guide.flow24.main() }.verifyLinesArbitraryTime( + "1: First at 136 ms from start", + "2: First at 231 ms from start", + "3: First at 333 ms from start", + "1: Second at 639 ms from start", + "2: Second at 732 ms from start", + "3: Second at 833 ms from start" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow25() { + test("KotlinxCoroutinesGuideFlow25") { kotlinx.coroutines.guide.flow25.main() }.verifyLinesArbitraryTime( + "1: First at 142 ms from start", + "2: First at 322 ms from start", + "3: First at 425 ms from start", + "3: Second at 931 ms from start" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow26() { + test("KotlinxCoroutinesGuideFlow26") { kotlinx.coroutines.guide.flow26.main() }.verifyLines( + "Emitting 1", + "1", + "Emitting 2", + "2", + "Caught java.lang.IllegalStateException: Collected 2" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow27() { + test("KotlinxCoroutinesGuideFlow27") { kotlinx.coroutines.guide.flow27.main() }.verifyLines( + "Emitting 1", + "string 1", + "Emitting 2", + "Caught java.lang.IllegalStateException: Crashed on 2" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow28() { + test("KotlinxCoroutinesGuideFlow28") { kotlinx.coroutines.guide.flow28.main() }.verifyLines( + "Emitting 1", + "string 1", + "Emitting 2", + "Caught java.lang.IllegalStateException: Crashed on 2" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow29() { + test("KotlinxCoroutinesGuideFlow29") { kotlinx.coroutines.guide.flow29.main() }.verifyExceptions( + "Emitting 1", + "1", + "Emitting 2", + "Exception in thread \"main\" java.lang.IllegalStateException: Collected 2", + " at ..." + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow30() { + test("KotlinxCoroutinesGuideFlow30") { kotlinx.coroutines.guide.flow30.main() }.verifyExceptions( + "Emitting 1", + "1", + "Emitting 2", + "Caught java.lang.IllegalStateException: Collected 2" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow31() { + test("KotlinxCoroutinesGuideFlow31") { kotlinx.coroutines.guide.flow31.main() }.verifyLines( + "1", + "2", + "3", + "Done" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow32() { + test("KotlinxCoroutinesGuideFlow32") { kotlinx.coroutines.guide.flow32.main() }.verifyLines( + "1", + "2", + "3", + "Done" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow33() { + test("KotlinxCoroutinesGuideFlow33") { kotlinx.coroutines.guide.flow33.main() }.verifyLines( + "1", + "Flow completed exceptionally", + "Caught exception" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow34() { + test("KotlinxCoroutinesGuideFlow34") { kotlinx.coroutines.guide.flow34.main() }.verifyExceptions( + "1", + "Flow completed with null", + "Exception in thread \"main\" java.lang.IllegalStateException: Collected 2" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow35() { + test("KotlinxCoroutinesGuideFlow35") { kotlinx.coroutines.guide.flow35.main() }.verifyLines( + "Event: 1", + "Event: 2", + "Event: 3", + "Done" + ) + } + + @Test + fun testKotlinxCoroutinesGuideFlow36() { + test("KotlinxCoroutinesGuideFlow36") { kotlinx.coroutines.guide.flow36.main() }.verifyLines( + "Done", + "Event: 1", + "Event: 2", + "Event: 3" + ) + } +} diff --git a/kotlinx-coroutines-core/jvm/test/guide/test/TestUtil.kt b/kotlinx-coroutines-core/jvm/test/guide/test/TestUtil.kt index 83150e1093..c72b60bd53 100644 --- a/kotlinx-coroutines-core/jvm/test/guide/test/TestUtil.kt +++ b/kotlinx-coroutines-core/jvm/test/guide/test/TestUtil.kt @@ -132,6 +132,7 @@ private fun sanitize(s: String, mode: SanitizeMode): String { res = res.replace(Regex("DefaultDispatcher-worker-[0-9]+"), "DefaultDispatcher") res = res.replace(Regex("RxComputationThreadPool-[0-9]+"), "RxComputationThreadPool") res = res.replace(Regex("Test( worker)?"), "main") + res = res.replace(Regex("@[0-9a-f]+"), "") // drop hex address } SanitizeMode.NONE -> {} } @@ -180,8 +181,17 @@ fun List.verifyLinesStartUnordered(vararg expected: String) = verify { } fun List.verifyExceptions(vararg expected: String) { - val actual = filter { !it.startsWith("\tat ") } - + val original = this + val actual = ArrayList().apply { + var except = false + for (line in original) { + when { + !except && line.startsWith("\tat") -> except = true + except && !line.startsWith("\t") && !line.startsWith("Caused by: ") -> except = false + } + if (!except) add(line) + } + } val n = minOf(actual.size, expected.size) for (i in 0 until n) { val exp = sanitize(expected[i], SanitizeMode.FLEXIBLE_THREAD)