hadihariri | 7db5553 | 2018-09-15 10:35:08 +0200 | [diff] [blame] | 1 | <!--- INCLUDE .*/example-([a-z]+)-([0-9a-z]+)\.kt |
| 2 | /* |
| 3 | * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. |
| 4 | */ |
| 5 | |
| 6 | // This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. |
| 7 | package kotlinx.coroutines.experimental.guide.$$1$$2 |
| 8 | |
| 9 | import kotlinx.coroutines.experimental.* |
| 10 | import kotlinx.coroutines.experimental.channels.* |
| 11 | --> |
| 12 | <!--- KNIT ../core/kotlinx-coroutines-core/test/guide/.*\.kt --> |
| 13 | <!--- TEST_OUT ../core/kotlinx-coroutines-core/test/guide/test/ChannelsGuideTest.kt |
| 14 | // This file was automatically generated from coroutines-guide.md by Knit tool. Do not edit. |
| 15 | package kotlinx.coroutines.experimental.guide.test |
| 16 | |
| 17 | import org.junit.Test |
| 18 | |
| 19 | class ChannelsGuideTest { |
| 20 | --> |
| 21 | ## Table of contents |
| 22 | |
| 23 | <!--- TOC --> |
| 24 | |
| 25 | * [Channels (experimental)](#channels-(experimental)) |
| 26 | * [Channel basics](#channel-basics) |
| 27 | * [Closing and iteration over channels](#closing-and-iteration-over-channels) |
| 28 | * [Building channel producers](#building-channel-producers) |
| 29 | * [Pipelines](#pipelines) |
| 30 | * [Prime numbers with pipeline](#prime-numbers-with-pipeline) |
| 31 | * [Fan-out](#fan-out) |
| 32 | * [Fan-in](#fan-in) |
| 33 | * [Buffered channels](#buffered-channels) |
| 34 | * [Channels are fair](#channels-are-fair) |
| 35 | * [Ticker channels](#ticker-channels) |
| 36 | |
| 37 | <!--- END_TOC --> |
| 38 | |
| 39 | |
| 40 | |
| 41 | |
| 42 | |
| 43 | ## Channels (experimental) |
| 44 | |
| 45 | Deferred values provide a convenient way to transfer a single value between coroutines. |
| 46 | Channels provide a way to transfer a stream of values. |
| 47 | |
| 48 | > Channels are an experimental feature of `kotlinx.coroutines`. Their API is expected to |
| 49 | evolve in the upcoming updates of the `kotlinx.coroutines` library with potentially |
| 50 | breaking changes. |
| 51 | |
| 52 | ### Channel basics |
| 53 | |
| 54 | A [Channel] is conceptually very similar to `BlockingQueue`. One key difference is that |
| 55 | instead of a blocking `put` operation it has a suspending [send][SendChannel.send], and instead of |
| 56 | a blocking `take` operation it has a suspending [receive][ReceiveChannel.receive]. |
| 57 | |
| 58 | ```kotlin |
| 59 | fun main(args: Array<String>) = runBlocking<Unit> { |
| 60 | val channel = Channel<Int>() |
| 61 | launch { |
| 62 | // this might be heavy CPU-consuming computation or async logic, we'll just send five squares |
| 63 | for (x in 1..5) channel.send(x * x) |
| 64 | } |
| 65 | // here we print five received integers: |
| 66 | repeat(5) { println(channel.receive()) } |
| 67 | println("Done!") |
| 68 | } |
| 69 | ``` |
| 70 | |
| 71 | > You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-channel-01.kt) |
| 72 | |
| 73 | The output of this code is: |
| 74 | |
| 75 | ```text |
| 76 | 1 |
| 77 | 4 |
| 78 | 9 |
| 79 | 16 |
| 80 | 25 |
| 81 | Done! |
| 82 | ``` |
| 83 | |
| 84 | <!--- TEST --> |
| 85 | |
| 86 | ### Closing and iteration over channels |
| 87 | |
| 88 | Unlike a queue, a channel can be closed to indicate that no more elements are coming. |
| 89 | On the receiver side it is convenient to use a regular `for` loop to receive elements |
| 90 | from the channel. |
| 91 | |
| 92 | Conceptually, a [close][SendChannel.close] is like sending a special close token to the channel. |
| 93 | The iteration stops as soon as this close token is received, so there is a guarantee |
| 94 | that all previously sent elements before the close are received: |
| 95 | |
| 96 | ```kotlin |
| 97 | fun main(args: Array<String>) = runBlocking<Unit> { |
| 98 | val channel = Channel<Int>() |
| 99 | launch { |
| 100 | for (x in 1..5) channel.send(x * x) |
| 101 | channel.close() // we're done sending |
| 102 | } |
| 103 | // here we print received values using `for` loop (until the channel is closed) |
| 104 | for (y in channel) println(y) |
| 105 | println("Done!") |
| 106 | } |
| 107 | ``` |
| 108 | |
| 109 | > You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-channel-02.kt) |
| 110 | |
| 111 | <!--- TEST |
| 112 | 1 |
| 113 | 4 |
| 114 | 9 |
| 115 | 16 |
| 116 | 25 |
| 117 | Done! |
| 118 | --> |
| 119 | |
| 120 | ### Building channel producers |
| 121 | |
| 122 | The pattern where a coroutine is producing a sequence of elements is quite common. |
| 123 | This is a part of _producer-consumer_ pattern that is often found in concurrent code. |
| 124 | You could abstract such a producer into a function that takes channel as its parameter, but this goes contrary |
| 125 | to common sense that results must be returned from functions. |
| 126 | |
| 127 | There is a convenience coroutine builder named [produce] that makes it easy to do it right on producer side, |
| 128 | and an extension function [consumeEach], that replaces a `for` loop on the consumer side: |
| 129 | |
| 130 | ```kotlin |
| 131 | fun CoroutineScope.produceSquares() = produce<Int> { |
| 132 | for (x in 1..5) send(x * x) |
| 133 | } |
| 134 | |
| 135 | fun main(args: Array<String>) = runBlocking<Unit> { |
| 136 | val squares = produceSquares() |
| 137 | squares.consumeEach { println(it) } |
| 138 | println("Done!") |
| 139 | } |
| 140 | ``` |
| 141 | |
| 142 | > You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-channel-03.kt) |
| 143 | |
| 144 | <!--- TEST |
| 145 | 1 |
| 146 | 4 |
| 147 | 9 |
| 148 | 16 |
| 149 | 25 |
| 150 | Done! |
| 151 | --> |
| 152 | |
| 153 | ### Pipelines |
| 154 | |
| 155 | A pipeline is a pattern where one coroutine is producing, possibly infinite, stream of values: |
| 156 | |
| 157 | ```kotlin |
| 158 | fun CoroutineScope.produceNumbers() = produce<Int> { |
| 159 | var x = 1 |
| 160 | while (true) send(x++) // infinite stream of integers starting from 1 |
| 161 | } |
| 162 | ``` |
| 163 | |
| 164 | And another coroutine or coroutines are consuming that stream, doing some processing, and producing some other results. |
| 165 | In the below example the numbers are just squared: |
| 166 | |
| 167 | ```kotlin |
| 168 | fun CoroutineScope.square(numbers: ReceiveChannel<Int>) = produce<Int> { |
| 169 | for (x in numbers) send(x * x) |
| 170 | } |
| 171 | ``` |
| 172 | |
| 173 | The main code starts and connects the whole pipeline: |
| 174 | |
| 175 | ```kotlin |
| 176 | fun main(args: Array<String>) = runBlocking<Unit> { |
| 177 | val numbers = produceNumbers() // produces integers from 1 and on |
| 178 | val squares = square(numbers) // squares integers |
| 179 | for (i in 1..5) println(squares.receive()) // print first five |
| 180 | println("Done!") // we are done |
| 181 | coroutineContext.cancelChildren() // cancel children coroutines |
| 182 | } |
| 183 | ``` |
| 184 | |
| 185 | > You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-channel-04.kt) |
| 186 | |
| 187 | <!--- TEST |
| 188 | 1 |
| 189 | 4 |
| 190 | 9 |
| 191 | 16 |
| 192 | 25 |
| 193 | Done! |
| 194 | --> |
| 195 | |
| 196 | > All functions that create coroutines are defined as extensions on [CoroutineScope], |
| 197 | so that we can rely on [structured concurrency](#structured-concurrency) to make |
| 198 | sure that we don't have lingering global coroutines in our application. |
| 199 | |
| 200 | ### Prime numbers with pipeline |
| 201 | |
| 202 | Let's take pipelines to the extreme with an example that generates prime numbers using a pipeline |
| 203 | of coroutines. We start with an infinite sequence of numbers. |
| 204 | |
| 205 | <!--- INCLUDE |
| 206 | import kotlin.coroutines.experimental.* |
| 207 | --> |
| 208 | |
| 209 | ```kotlin |
| 210 | fun CoroutineScope.numbersFrom(start: Int) = produce<Int> { |
| 211 | var x = start |
| 212 | while (true) send(x++) // infinite stream of integers from start |
| 213 | } |
| 214 | ``` |
| 215 | |
| 216 | The following pipeline stage filters an incoming stream of numbers, removing all the numbers |
| 217 | that are divisible by the given prime number: |
| 218 | |
| 219 | ```kotlin |
| 220 | fun CoroutineScope.filter(numbers: ReceiveChannel<Int>, prime: Int) = produce<Int> { |
| 221 | for (x in numbers) if (x % prime != 0) send(x) |
| 222 | } |
| 223 | ``` |
| 224 | |
| 225 | Now we build our pipeline by starting a stream of numbers from 2, taking a prime number from the current channel, |
| 226 | and launching new pipeline stage for each prime number found: |
| 227 | |
| 228 | ``` |
| 229 | numbersFrom(2) -> filter(2) -> filter(3) -> filter(5) -> filter(7) ... |
| 230 | ``` |
| 231 | |
| 232 | The following example prints the first ten prime numbers, |
| 233 | running the whole pipeline in the context of the main thread. Since all the coroutines are launched in |
| 234 | the scope of the main [runBlocking] coroutine |
| 235 | we don't have to keep an explicit list of all the coroutines we have started. |
| 236 | We use [cancelChildren][kotlin.coroutines.experimental.CoroutineContext.cancelChildren] |
| 237 | extension function to cancel all the children coroutines after we have printed |
| 238 | the first ten prime numbers. |
| 239 | |
| 240 | ```kotlin |
| 241 | fun main(args: Array<String>) = runBlocking<Unit> { |
| 242 | var cur = numbersFrom(2) |
| 243 | for (i in 1..10) { |
| 244 | val prime = cur.receive() |
| 245 | println(prime) |
| 246 | cur = filter(cur, prime) |
| 247 | } |
| 248 | coroutineContext.cancelChildren() // cancel all children to let main finish |
| 249 | } |
| 250 | ``` |
| 251 | |
| 252 | > You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-channel-05.kt) |
| 253 | |
| 254 | The output of this code is: |
| 255 | |
| 256 | ```text |
| 257 | 2 |
| 258 | 3 |
| 259 | 5 |
| 260 | 7 |
| 261 | 11 |
| 262 | 13 |
| 263 | 17 |
| 264 | 19 |
| 265 | 23 |
| 266 | 29 |
| 267 | ``` |
| 268 | |
| 269 | <!--- TEST --> |
| 270 | |
| 271 | Note, that you can build the same pipeline using |
| 272 | [`buildIterator`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/build-iterator.html) |
| 273 | coroutine builder from the standard library. |
| 274 | Replace `produce` with `buildIterator`, `send` with `yield`, `receive` with `next`, |
| 275 | `ReceiveChannel` with `Iterator`, and get rid of the coroutine scope. You will not need `runBlocking` either. |
| 276 | However, the benefit of a pipeline that uses channels as shown above is that it can actually use |
| 277 | multiple CPU cores if you run it in [Dispatchers.Default] context. |
| 278 | |
| 279 | Anyway, this is an extremely impractical way to find prime numbers. In practice, pipelines do involve some |
| 280 | other suspending invocations (like asynchronous calls to remote services) and these pipelines cannot be |
| 281 | built using `buildSequence`/`buildIterator`, because they do not allow arbitrary suspension, unlike |
| 282 | `produce`, which is fully asynchronous. |
| 283 | |
| 284 | ### Fan-out |
| 285 | |
| 286 | Multiple coroutines may receive from the same channel, distributing work between themselves. |
| 287 | Let us start with a producer coroutine that is periodically producing integers |
| 288 | (ten numbers per second): |
| 289 | |
| 290 | ```kotlin |
| 291 | fun CoroutineScope.produceNumbers() = produce<Int> { |
| 292 | var x = 1 // start from 1 |
| 293 | while (true) { |
| 294 | send(x++) // produce next |
| 295 | delay(100) // wait 0.1s |
| 296 | } |
| 297 | } |
| 298 | ``` |
| 299 | |
| 300 | Then we can have several processor coroutines. In this example, they just print their id and |
| 301 | received number: |
| 302 | |
| 303 | ```kotlin |
| 304 | fun CoroutineScope.launchProcessor(id: Int, channel: ReceiveChannel<Int>) = launch { |
| 305 | for (msg in channel) { |
| 306 | println("Processor #$id received $msg") |
| 307 | } |
| 308 | } |
| 309 | ``` |
| 310 | |
| 311 | Now let us launch five processors and let them work for almost a second. See what happens: |
| 312 | |
| 313 | ```kotlin |
| 314 | fun main(args: Array<String>) = runBlocking<Unit> { |
| 315 | val producer = produceNumbers() |
| 316 | repeat(5) { launchProcessor(it, producer) } |
| 317 | delay(950) |
| 318 | producer.cancel() // cancel producer coroutine and thus kill them all |
| 319 | } |
| 320 | ``` |
| 321 | |
| 322 | > You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-channel-06.kt) |
| 323 | |
| 324 | The output will be similar to the the following one, albeit the processor ids that receive |
| 325 | each specific integer may be different: |
| 326 | |
| 327 | ``` |
| 328 | Processor #2 received 1 |
| 329 | Processor #4 received 2 |
| 330 | Processor #0 received 3 |
| 331 | Processor #1 received 4 |
| 332 | Processor #3 received 5 |
| 333 | Processor #2 received 6 |
| 334 | Processor #4 received 7 |
| 335 | Processor #0 received 8 |
| 336 | Processor #1 received 9 |
| 337 | Processor #3 received 10 |
| 338 | ``` |
| 339 | |
| 340 | <!--- TEST lines.size == 10 && lines.withIndex().all { (i, line) -> line.startsWith("Processor #") && line.endsWith(" received ${i + 1}") } --> |
| 341 | |
| 342 | Note, that cancelling a producer coroutine closes its channel, thus eventually terminating iteration |
| 343 | over the channel that processor coroutines are doing. |
| 344 | |
| 345 | Also, pay attention to how we explicitly iterate over channel with `for` loop to perform fan-out in `launchProcessor` code. |
| 346 | Unlike `consumeEach`, this `for` loop pattern is perfectly safe to use from multiple coroutines. If one of the processor |
| 347 | coroutines fails, then others would still be processing the channel, while a processor that is written via `consumeEach` |
| 348 | always consumes (cancels) the underlying channel on its normal or abnormal completion. |
| 349 | |
| 350 | ### Fan-in |
| 351 | |
| 352 | Multiple coroutines may send to the same channel. |
| 353 | For example, let us have a channel of strings, and a suspending function that |
| 354 | repeatedly sends a specified string to this channel with a specified delay: |
| 355 | |
| 356 | <!--- INCLUDE |
| 357 | import kotlin.coroutines.experimental.* |
| 358 | --> |
| 359 | |
| 360 | ```kotlin |
| 361 | suspend fun sendString(channel: SendChannel<String>, s: String, time: Long) { |
| 362 | while (true) { |
| 363 | delay(time) |
| 364 | channel.send(s) |
| 365 | } |
| 366 | } |
| 367 | ``` |
| 368 | |
| 369 | Now, let us see what happens if we launch a couple of coroutines sending strings |
| 370 | (in this example we launch them in the context of the main thread as main coroutine's children): |
| 371 | |
| 372 | ```kotlin |
| 373 | fun main(args: Array<String>) = runBlocking<Unit> { |
| 374 | val channel = Channel<String>() |
| 375 | launch { sendString(channel, "foo", 200L) } |
| 376 | launch { sendString(channel, "BAR!", 500L) } |
| 377 | repeat(6) { // receive first six |
| 378 | println(channel.receive()) |
| 379 | } |
| 380 | coroutineContext.cancelChildren() // cancel all children to let main finish |
| 381 | } |
| 382 | ``` |
| 383 | |
| 384 | > You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-channel-07.kt) |
| 385 | |
| 386 | The output is: |
| 387 | |
| 388 | ```text |
| 389 | foo |
| 390 | foo |
| 391 | BAR! |
| 392 | foo |
| 393 | foo |
| 394 | BAR! |
| 395 | ``` |
| 396 | |
| 397 | <!--- TEST --> |
| 398 | |
| 399 | ### Buffered channels |
| 400 | |
| 401 | The channels shown so far had no buffer. Unbuffered channels transfer elements when sender and receiver |
| 402 | meet each other (aka rendezvous). If send is invoked first, then it is suspended until receive is invoked, |
| 403 | if receive is invoked first, it is suspended until send is invoked. |
| 404 | |
| 405 | Both [Channel()] factory function and [produce] builder take an optional `capacity` parameter to |
| 406 | specify _buffer size_. Buffer allows senders to send multiple elements before suspending, |
| 407 | similar to the `BlockingQueue` with a specified capacity, which blocks when buffer is full. |
| 408 | |
| 409 | Take a look at the behavior of the following code: |
| 410 | |
| 411 | <!--- INCLUDE |
| 412 | import kotlin.coroutines.experimental.* |
| 413 | --> |
| 414 | |
| 415 | ```kotlin |
| 416 | fun main(args: Array<String>) = runBlocking<Unit> { |
| 417 | val channel = Channel<Int>(4) // create buffered channel |
| 418 | val sender = launch { // launch sender coroutine |
| 419 | repeat(10) { |
| 420 | println("Sending $it") // print before sending each element |
| 421 | channel.send(it) // will suspend when buffer is full |
| 422 | } |
| 423 | } |
| 424 | // don't receive anything... just wait.... |
| 425 | delay(1000) |
| 426 | sender.cancel() // cancel sender coroutine |
| 427 | } |
| 428 | ``` |
| 429 | |
| 430 | > You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-channel-08.kt) |
| 431 | |
| 432 | It prints "sending" _five_ times using a buffered channel with capacity of _four_: |
| 433 | |
| 434 | ```text |
| 435 | Sending 0 |
| 436 | Sending 1 |
| 437 | Sending 2 |
| 438 | Sending 3 |
| 439 | Sending 4 |
| 440 | ``` |
| 441 | |
| 442 | <!--- TEST --> |
| 443 | |
| 444 | The first four elements are added to the buffer and the sender suspends when trying to send the fifth one. |
| 445 | |
| 446 | ### Channels are fair |
| 447 | |
| 448 | Send and receive operations to channels are _fair_ with respect to the order of their invocation from |
| 449 | multiple coroutines. They are served in first-in first-out order, e.g. the first coroutine to invoke `receive` |
| 450 | gets the element. In the following example two coroutines "ping" and "pong" are |
| 451 | receiving the "ball" object from the shared "table" channel. |
| 452 | |
| 453 | <!--- INCLUDE |
| 454 | import kotlin.coroutines.experimental.* |
| 455 | --> |
| 456 | |
| 457 | ```kotlin |
| 458 | data class Ball(var hits: Int) |
| 459 | |
| 460 | fun main(args: Array<String>) = runBlocking<Unit> { |
| 461 | val table = Channel<Ball>() // a shared table |
| 462 | launch { player("ping", table) } |
| 463 | launch { player("pong", table) } |
| 464 | table.send(Ball(0)) // serve the ball |
| 465 | delay(1000) // delay 1 second |
| 466 | coroutineContext.cancelChildren() // game over, cancel them |
| 467 | } |
| 468 | |
| 469 | suspend fun player(name: String, table: Channel<Ball>) { |
| 470 | for (ball in table) { // receive the ball in a loop |
| 471 | ball.hits++ |
| 472 | println("$name $ball") |
| 473 | delay(300) // wait a bit |
| 474 | table.send(ball) // send the ball back |
| 475 | } |
| 476 | } |
| 477 | ``` |
| 478 | |
| 479 | > You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-channel-09.kt) |
| 480 | |
| 481 | The "ping" coroutine is started first, so it is the first one to receive the ball. Even though "ping" |
| 482 | coroutine immediately starts receiving the ball again after sending it back to the table, the ball gets |
| 483 | received by the "pong" coroutine, because it was already waiting for it: |
| 484 | |
| 485 | ```text |
| 486 | ping Ball(hits=1) |
| 487 | pong Ball(hits=2) |
| 488 | ping Ball(hits=3) |
| 489 | pong Ball(hits=4) |
| 490 | ``` |
| 491 | |
| 492 | <!--- TEST --> |
| 493 | |
| 494 | Note, that sometimes channels may produce executions that look unfair due to the nature of the executor |
| 495 | that is being used. See [this issue](https://github.com/Kotlin/kotlinx.coroutines/issues/111) for details. |
| 496 | |
| 497 | ### Ticker channels |
| 498 | |
| 499 | Ticker channel is a special rendezvous channel that produces `Unit` every time given delay passes since last consumption from this channel. |
| 500 | Though it may seem to be useless standalone, it is a useful building block to create complex time-based [produce] |
| 501 | pipelines and operators that do windowing and other time-dependent processing. |
| 502 | Ticker channel can be used in [select] to perform "on tick" action. |
| 503 | |
| 504 | To create such channel use a factory method [ticker]. |
| 505 | To indicate that no further elements are needed use [ReceiveChannel.cancel] method on it. |
| 506 | |
| 507 | Now let's see how it works in practice: |
| 508 | |
| 509 | ```kotlin |
| 510 | fun main(args: Array<String>) = runBlocking<Unit> { |
| 511 | val tickerChannel = ticker(delay = 100, initialDelay = 0) // create ticker channel |
| 512 | var nextElement = withTimeoutOrNull(1) { tickerChannel.receive() } |
| 513 | println("Initial element is available immediately: $nextElement") // initial delay hasn't passed yet |
| 514 | |
| 515 | nextElement = withTimeoutOrNull(50) { tickerChannel.receive() } // all subsequent elements has 100ms delay |
| 516 | println("Next element is not ready in 50 ms: $nextElement") |
| 517 | |
| 518 | nextElement = withTimeoutOrNull(60) { tickerChannel.receive() } |
| 519 | println("Next element is ready in 100 ms: $nextElement") |
| 520 | |
| 521 | // Emulate large consumption delays |
| 522 | println("Consumer pauses for 150ms") |
| 523 | delay(150) |
| 524 | // Next element is available immediately |
| 525 | nextElement = withTimeoutOrNull(1) { tickerChannel.receive() } |
| 526 | println("Next element is available immediately after large consumer delay: $nextElement") |
| 527 | // Note that the pause between `receive` calls is taken into account and next element arrives faster |
| 528 | nextElement = withTimeoutOrNull(60) { tickerChannel.receive() } |
| 529 | println("Next element is ready in 50ms after consumer pause in 150ms: $nextElement") |
| 530 | |
| 531 | tickerChannel.cancel() // indicate that no more elements are needed |
| 532 | } |
| 533 | ``` |
| 534 | |
| 535 | > You can get full code [here](../core/kotlinx-coroutines-core/test/guide/example-channel-10.kt) |
| 536 | |
| 537 | It prints following lines: |
| 538 | |
| 539 | ```text |
| 540 | Initial element is available immediately: kotlin.Unit |
| 541 | Next element is not ready in 50 ms: null |
| 542 | Next element is ready in 100 ms: kotlin.Unit |
| 543 | Consumer pauses for 150ms |
| 544 | Next element is available immediately after large consumer delay: kotlin.Unit |
| 545 | Next element is ready in 50ms after consumer pause in 150ms: kotlin.Unit |
| 546 | ``` |
| 547 | |
| 548 | <!--- TEST --> |
| 549 | |
| 550 | Note that [ticker] is aware of possible consumer pauses and, by default, adjusts next produced element |
| 551 | delay if a pause occurs, trying to maintain a fixed rate of produced elements. |
| 552 | |
| 553 | Optionally, a `mode` parameter equal to [TickerMode.FIXED_DELAY] can be specified to maintain a fixed |
| 554 | delay between elements. |
| 555 | |
| 556 | |
| 557 | <!--- MODULE kotlinx-coroutines-core --> |
Roman Elizarov | 99c28aa | 2018-09-23 18:42:36 +0300 | [diff] [blame^] | 558 | <!--- INDEX kotlinx.coroutines.experimental --> |
| 559 | [CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-coroutine-scope/index.html |
| 560 | [runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/run-blocking.html |
| 561 | [kotlin.coroutines.experimental.CoroutineContext.cancelChildren]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/kotlin.coroutines.experimental.-coroutine-context/cancel-children.html |
| 562 | [Dispatchers.Default]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-dispatchers/-default.html |
hadihariri | 7db5553 | 2018-09-15 10:35:08 +0200 | [diff] [blame] | 563 | <!--- INDEX kotlinx.coroutines.experimental.channels --> |
| 564 | [Channel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-channel/index.html |
| 565 | [SendChannel.send]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-send-channel/send.html |
| 566 | [ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-receive-channel/receive.html |
| 567 | [SendChannel.close]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-send-channel/close.html |
| 568 | [produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/produce.html |
| 569 | [consumeEach]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/consume-each.html |
| 570 | [Channel()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-channel.html |
| 571 | [ticker]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/ticker.html |
| 572 | [ReceiveChannel.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-receive-channel/cancel.html |
| 573 | [TickerMode.FIXED_DELAY]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.channels/-ticker-mode/-f-i-x-e-d_-d-e-l-a-y.html |
Roman Elizarov | 99c28aa | 2018-09-23 18:42:36 +0300 | [diff] [blame^] | 574 | <!--- INDEX kotlinx.coroutines.experimental.selects --> |
| 575 | [select]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental.selects/select.html |
hadihariri | 7db5553 | 2018-09-15 10:35:08 +0200 | [diff] [blame] | 576 | <!--- END --> |