코루틴은 가장 먼저 완료되는 코루틴의 결과를 기다리는 select 함수를 제공합니다.
또한 여러 개의 채널 중 버퍼에 남은 공간이 있는 채널을 먼저 확인하여 데이터를 보내거나, 이용 가능한 원소가 있는 채널로부터 데이터를 받을 수 있는지 여부도 확인할 수 있습니다.
여러 개의 소스에 데이터를 요청한 뒤 가장 빠른 응답만 얻는 경우를 코루틴만을 이용해서 생각해보면 async 코루틴 빌더로 여러개의 데이터를 병렬적으로 호출한 뒤 각 코루틴에서 await()로 기다리고 응답이 오면 존재하는 모든 코루틴을 취소 하는 방향으로 설계를 해야합니다. 하지만 select에서는 함수를 표현식으로 사용하고 표현식 내부에서 값을 기다리는 것만으로 처리할 수 있습니다.
select 내부에서는 셀렉트 표현식에서 나올 수 있는 결괏값을 명시하는 Deffered 값의 OnAwait 함수를 호출합니다.
fun main() = runBlocking {
println(askMultipleForData())
}
suspend fun requestData1(): String {
delay(10_000)
return "Data1"
}
suspend fun requestData2(): String {
delay(1000)
return "Data2"
}
val scope = CoroutineScope(SupervisorJob())
suspend fun askMultipleForData(): String {
val deferredData1 = scope.async { requestData1() }
val deferredData2 = scope.async { requestData2() }
return select {
deferredData1.onAwait { it }
deferredData2.onAwait { it }
}
}
select 함수는 채널에서도 사용할 수 있습니다.
onReceive: 채널이 값을 가지고 있을 때 선택 됩니다. 값을 받은 뒤 람다식의 인자로 사용합니다. onReceive가 선택되었을 때, selec는 람다식의 결괏값을 반환합니다.onReceiveCatcing: 채널이 값을 가지고 있거나 닫혔을 때 선택됩니다. 값을 나타내거나 채널이 닫혔다는 걸 알려주는 ChannelResult를 받으며, 이 값을 람다식의 인자로 사용합니다. onReceiveCatcing이 선택되었을 때, select는 람다식의 결괏값을 반환합니다.onSend: 채널의 버퍼에 공간이 있을 때 선택됩니다. 채널에 값을 보낸 뒤, 채널의 참조값으로 람다식을 수행합니다. onSend가 선택 되었을 때, select는 Unit을 반환합니다.fun main() = runBlocking {
val fooChannel = produceSting("foo", 210L)
val barChannel = produceSting("bar", 500L)
repeat(7) {
select {
fooChannel.onReceive {
println("From fooChannel: $it")
}
barChannel.onReceive {
println("From barChannel: $it")
}
}
}
fooChannel.cancel()
barChannel.cancel()
}
suspend fun CoroutineScope.produceSting(
s: String,
time: Long
) = produce {
while (true) {
delay(time)
send(s)
}
}
onReceive 이용해서 값을 가지고 있는 채널에서 값을 가져와 출력하는 코드 입니다.
fun main(): Unit = runBlocking {
val c1 = Channel<Char>(capacity = 2)
val c2 = Channel<Char>(capacity = 2)
launch {
for (c in 'A'..'H') {
delay(400)
select<Unit> {
c1.onSend(c) { println("Sent $c to 1") }
c2.onSend(c) { println("Sent $c to 2") }
}
}
}
launch {
while (true) {
delay(1000)
val c = select<String> {
c1.onReceive { "$it from 1" }
c2.onReceive { "$it from 2" }
}
println("Received $c")
}
}
}
onSen를 통해 버퍼에 공간이 남아있는 채널을 선택해서 원소를 보내는 예시 코드 입니다.