Channels

The async function returns an instance of the Deferred class that allows us to compute a single value. If we need to transfer a sequence of values between coroutines, we can use channels.

A channel is an interface that looks as follows:

public interface Channel<E> : SendChannel<E>, ReceiveChannel<E> {
//.....
}

The SendChannel interface looks as follows:

public interface SendChannel<in E> {

@ExperimentalCoroutinesApi
public val isClosedForSend: Boolean

@ExperimentalCoroutinesApi
public val isFull: Boolean

public suspend fun send(element: E)

public val onSend: SelectClause2<E, SendChannel<E>>

public fun offer(element: E): Boolean

public fun close(cause: Throwable? = null): Boolean

@ExperimentalCoroutinesApi
public fun invokeOnClose(handler: (cause: Throwable?) -> Unit)
}

The SendChannel interface contains the send method that takes a parameter and adds it to this channel. The isFull property is true if this channel already contains a value. In this case, the send function suspends the caller until the contained value is not consumed.

A channel can be closed by invoking the close method. In this case, the isClosedForSend property is true, and the send method throws an exception.

While the SendChannel interface allows us to put a value into a channel, the ReceiveChannel interface allows us to get the value from the channel. The ReceiveChannel interface looks as follows:

public interface ReceiveChannel<out E> {

@ExperimentalCoroutinesApi
public val isClosedForReceive: Boolean

@ExperimentalCoroutinesApi
public val isEmpty: Boolean

public suspend fun receive(): E

public val onReceive: SelectClause1<E>

@ExperimentalCoroutinesApi
public suspend fun receiveOrNull(): E?

@ExperimentalCoroutinesApi
public val onReceiveOrNull: SelectClause1<E?>

public fun poll(): E?

public operator fun iterator(): ChannelIterator<E>

public fun cancel(): Boolean

@ExperimentalCoroutinesApi
public fun cancel(cause: Throwable? = null): Boolean
}

The receiveOrNull() method returns and removes an element from this channel, or returns null if the isClosedForReceive property is true. The ReceiveChannel contains the iterator method, and can be used in the for loop.

Let's look at the following example code:

fun channelBasics() = runBlocking<Unit> {
val channel = Channel<Int>()
launch {
println("send 0 ${Date().toGMTString()}")
channel.send(0)
delay(1000)
println("send 1 ${Date().toGMTString()}")
channel.send(1)
}
delay(3000)
val theFirstElement = channel.receive()
println("receive $theFirstElement ${Date().toGMTString()}")
delay(4000)
val theSecondElement = channel.receive()
println("receive $theSecondElement ${Date().toGMTString()}")
}

In the preceding example, we sent two values by a channel and received those values. We also used the delay function to show that an operation takes some time.

The output looks as follows:

send 0 21 Oct 2018 13:30:12 GMT
receive 0 21 Oct 2018 13:30:15 GMT
send 1 21 Oct 2018 13:30:16 GMT
receive 1 21 Oct 2018 13:30:19 GMT

This output shows that the send function suspends a coroutine until a value is consumed.

We can use the for loop to receive values from a channel, as follows:

fun channelIterator() = runBlocking<Unit> {
val channel = Channel<Int>()
launch {
(0..5).forEach {
channel.send(it)
}
}
for (value in channel) {
println(value)
}
}

The output looks as follows:

 0
1
2
3
4
5
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.21.158