Flow 취소하기
Flow는 Coroutines의 기본적인 협력적인 취소를 따른다. 일반적으로, 취소 가능한 일시중단 함수(delay 같은)에서 Flow가 일시중단될 때 Flow로부터 값을 수집하는 것이 취소될 수 있다. 다음의 예는 Flow가 withTimeoutOrNull 블록에서 실행될 때, Flow가 시간 초과에 따라 어떻게 취소되고 코드 실행이 중지되는지 보여준다 :
fun simple(): Flow<Int> = flow {
for (i in 1..3) {
delay(100)
println("Emitting $i")
emit(i)
}
}
fun main() = runBlocking<Unit> {
withTimeoutOrNull(250) { // Timeout after 250ms
simple().collect { value -> println(value) }
}
println("Done")
}
📌 전체 코드는 이곳에서 확인할 수 있습니다.
simple 함수의 flow에서 2개의 숫자만 방출되고, 다음과 같은 출력*1을 만드는 것에 주목하자 :
Emitting 1
1
Emitting 2
2
Done
📌 자세한 사항을 알고 싶으면 Flow cancellation checks 섹션을 확인하자.
📖 아래 내용은 독자의 이해를 위해 번역자가 추가한 글입니다.
*1. simple 함수 내부의 flow는 100ms을 기다리고 emit이 실행된다. Timeout 시간이 250ms이므로 2번째 것을 방출시키고 나서는 실행이 취소된다.
이 글은 Coroutines 공식 문서를 번역한 글입니다.
원문 : Asynchronous Flow - Flow cancellation basics
원문 최종 수정 : 2022년 9월 28일
Flow 빌더
이전 예제들의 flow { ... } 빌더는 가장 기본적인 빌더이다. Flow를 선언할 수 있는 다른 빌더들도 있다.
- flowOf 빌더는 정해진 값의 세트를 방출하는 Flow를 정의한다.
- 다양한 Collection들과 Sequence들은 .asFlow() 확장 함수를 사용해 Flow로 변환될 수 있다.
(1..3).asFlow().collect { value -> println(value) }
📌 전체 코드는 이곳에서 확인할 수 있습니다.
이 글은 Coroutines 공식 문서를 번역한 글입니다.
원문 : Asynchronous Flow - Flow builders
원문 최종 수정 : 2022년 9월 28일
Flow 중간 연산자
Flow들은 Collections, Sequence와 같이 연산자를 이용해 변환될 수 있다. 중간 연산자는 업스트림 Flow에 적용되어 다운스트림 Flow를 반환한다. 이러한 연산자들은 Flow만큼 차갑다. 이러한 연산자를 호출하는 것은 그 자체로 일시 중단 함수가 아니다. 이는 빠르게 작동해 새롭게 변환된 Flow를 반환한다.
기본 연산자들은 map 혹은 filter와 같은 친숙한 이름을 가지고 있다. 이러한 연산자들과 Sequence들의 중요한 차이점은 이 연산자들 내부의 코드 블록에서는 일시 중단 함수를 호출 할 수 있다는 점이다.
예를 들어 요청을 수행하는 것이 오래 걸리는 작업이고 일시 중단 기능으로 구현되어 있는 경우에도, 요청들을 받는 Flow를 map 연산자를 사용해 결과에 매핑할 수 있다.
suspend fun performRequest(request: Int): String {
delay(1000) // imitate long-running asynchronous work
return "response $request"
}
fun main() = runBlocking<Unit> {
(1..3).asFlow() // a flow of requests
.map { request -> performRequest(request) }
.collect { response -> println(response) }
}
📌 전체 코드는 이곳에서 확인할 수 있습니다.
이는 다음과 같이 3줄의 결과를 만들어 내며, 각 줄은 이전 줄로부터 1초 후에 나타난다.
response 1
response 2
response 3
Transform 연산자
Flow의 변환 연산자들 중에서 가장 일반적인 것은 transform이다. 이는 map이나 filter와 같은 간단한 변환을 모방하거나 복잡한 변환들을 구현하는데 사용할 수 있다. transform 연산자를 사용하면 임의의 횟수 만큼 값을 emit 할 수 있다.
예를 들어, transform을 사용하면 오래걸리는 비동기 요청을 하기 전에 문자열을 emit하고 그 응답을 기다릴 수 있다.
(1..3).asFlow() // a flow of requests
.transform { request ->
emit("Making request $request")
emit(performRequest(request))
}
.collect { response -> println(response) }
📌 전체 코드는 이곳에서 확인할 수 있습니다.
이 코드의 결과는 다음과 같다.
Making request 1
response 1
Making request 2
response 2
Making request 3
response 3
크기 한정 연산자
take과 같은 크기 한정 중간 연산자들은 해당 임계치에 도달했을 때 flow의 실행을 취소한다. Coroutines의 취소는 언제나 Exception을 throw하여 수행되므로, try { ... } finally { ... } 같은 모든 리소스 관리를 위한 기능들은 취소에서 정상적으로 작동한다.
fun numbers(): Flow<Int> = flow {
try {
emit(1)
emit(2)
println("This line will not execute")
emit(3)
} finally {
println("Finally in numbers")
}
}
fun main() = runBlocking<Unit> {
numbers()
.take(2) // take only the first two
.collect { value -> println(value) }
}
📌 전체 코드는 이곳에서 확인할 수 있습니다.
numbers() 함수 내부의 flow { ... } body*1의 실행이 두 번째 숫자를 emit하고 멈추는 것을 이 코드의 결과에서 확실하게 볼 수 있다.
1
2
Finally in numbers
📖 아래 내용은 독자의 이해를 위해 번역자가 추가한 글입니다.
*1. body는 { ... } 내부의 코드를 뜻한다.
이 글은 Coroutines 공식 문서를 번역한 글입니다.
원문 : Asynchronous Flow - Intermediate flow operators
원문 최종 수정 : 2022년 9월 28일
목차로 돌아가기