Flow 2주차 2차 스터디 Flows are sequential 까지
Flows Are Cold
Flow가 cold stream 임을 설명한다.
우리는 Cold Stream 이 무엇인지 선행학습을 했으니 그냥 키워드만 들어도 어떤것인지 알 수 있었다.
결론적으로 공식문서에서는 Lazy하게 값을 뽑아온다는것을 설명하고있다.
Flow cancellation basics
Flow는 general cooperative cancellation of coroutines를 따른다고 하는데 직역하면 코루틴의 협력적 취소이다.
Coroutine의 취소되는 메커니즘은 생각보다 간단하지는 않다. -> 많은 내부적인 동작원리들을 알아야한다.
만약 코루틴의 cancelleation이 익숙치 않다면 이글을 보고오자
만약 글을 보고와서 예제를 살펴본다면 그냥 당연한소리 하면서 와닿을것이다
Flow builders
여태까지는 flow{} 이 빌더함수만 봐왔는데 가장 기본적인 방법이고 다른 방법들도 존재한다. 하지만 딱히 쓸모 있어 보이지는 않는다.
- flow
가장 기본적인 방법이다. 그냥 일반적인 사용법을 생각하면 될 것 같다.
- flowOf
이미 주어진(고정된) 값들로 Flow를 생성할때 쓰는 방법이라고 한다. 예시를 보면 이해는 가지만 딱히 안쓸것같다. 차라리 asFlow를 쓰지
fun main() = runBlocking {
val fruitsFlow = flowOf("Apple", "Banana", "Cherry")
fruitsFlow.collect { fruit ->
println(fruit)
}
}
//결과
//Apple
//Banana
//Cherry
- asFlow
다양한 컬렉션과 시퀀스를 asFlow() 확장함수를 통해 Flow로 변환가능하다. -> 사용되는 예시들보면서 익혀서 여기저기 쓰면 될것같다.
// Convert an integer range to a flow
(1..3).asFlow().collect { value -> println(value) }
Intermediate flow operators
중간 연산자를 설명하는 내용으로 일반적으로 collections 혹은 sequences에서 사용하던것을 떠올리면된다.
upStream을 변경하여 downStream 으로 내뱉는데 이것을 cold(lazy)하게 수행하며 그 자체로 suspending function은 아니지만
내부에서 suspending function 을 호출할 수 있다.
Transform operator
Transform 이라는 중간연산자가 있는데 이는 중간연산자 이지만 그 내부 블록안에서 값을 emit할 수 있다.(확장성 장난 아닐듯)
그래서 flow를 마음대로 조절할 수 있는데 값을 단순 변환이 아니라 더 많은 횟수 만큼 방출할 수 있다는것을 강조하고 있다.
(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
Size-limiting operators
Take 같은 사이즈를 제한하는 중간연산자의 경우 제한하는 상황에서 coroutine 를 Cancellation낸다. 즉 Exception을 throw하여 cancel하는것이다.
고로 리소스 정리용 함수인 Try Catch 등을 사용할 수 있다고 한다.(사용하는게 맞는지는 모르겠다...)
멧돼지 추가내용
중간 연산자가 굉장히 다양한것으로 알고있어 찾아봤더니 이런 것들이 있었다. 추후 각각의 사용예시를 적은 글을 작성하려한다.
- map: 각 값을 변환합니다.
- filter: 특정 조건을 만족하는 값만 통과시킵니다.
- transform: 각 값을 변환할 때 추가적인 효과를 부여할 수 있으며, 하나의 입력 값에 대해 여러 값을 내보낼 수도 있습니다.
- take: 지정된 수의 값을 내보낸 후 수집을 완료합니다.
- drop: 지정된 수의 값을 건너뛴 후 나머지 값을 내보냅니다.
- flatMapConcat: 각 값을 Flow로 변환하고, 이 Flow들을 연결하여 단일 Flow로 만듭니다.
- flatMapMerge: flatMapConcat과 유사하지만, 변환된 Flow들을 병렬로 수집합니다.
- flatMapLatest: 새로운 값이 들어올 때마다 이전에 변환된 Flow의 수집을 취소하고 최신 Flow만 수집합니다.
- buffer: 수집 작업과 발생 작업 사이에 버퍼를 두어 비동기로 실행될 수 있게 합니다.
- conflate: 느린 수집기를 가지고 있을 때, 새 값을 내보내기 위해 오래된 값을 버립니다.
- collectLatest: 새로운 값이 들어올 때마다 현재 수집 작업을 취소하고 최신 값부터 수집을 시작합니다.
- distinctUntilChanged: 연속적으로 중복된 값이 나오지 않게 합니다.
- onEach: 각 값을 수집하기 전에 부가적인 동작을 수행할 수 있게 합니다.
- zip: 두 Flow의 값들을 한 쌍으로 결합합니다.
- combine: 두 개 이상의 Flow를 결합하여, 각 Flow에서 새로운 값이 발생할 때마다 결합된 결과를 내보냅니다.
- withIndex: 값에 인덱스를 부여합니다.
- scan: 주어진 함수를 사용하여 값을 누적하고, 각 단계의 결과를 반환합니다.
Terminal flow operators
Flow를 수집 할 수 있도록 하는 함수를 터미널 연산자라 하며 이는 suspending 함수이다.
collect가 대표적인 연산자이고 더 다양한 것들이 있다.
공식문서에는 toList, toSet, first, single, reduce, fold 가 나와있는데 이는 이름만 봐도 용도가 어떤것인지 알 수 있을것이다.
하지만 더 많은 터미널 연산자가 있다. 추후 이것들 또한 정리하는 글을 한번써야겠다.
- collect: 모든 값들을 수집합니다. 가장 일반적인 터미널 연산자로, 람다를 통해 각 값에 대해 수행할 작업을 정의할 수 있습니다.
- toList / toSet: 수집된 값들을 리스트나 세트로 변환합니다.
- first: 첫 번째 값을 반환하고 Flow의 실행을 종료합니다.
- firstOrNull: 첫 번째 값을 반환하되, 값이 없으면 null을 반환합니다.
- single: 하나의 값만을 내보내고 기대하는 Flow에 대해 사용하며, 값이 하나가 아닐 경우 예외를 발생시킵니다.
- singleOrNull: 하나의 값만 내보낼 때 사용하며, 값이 없거나 하나 이상일 때는 null을 반환합니다.
- reduce: 주어진 연산을 사용하여 모든 값을 하나로 누적합니다.
- fold: reduce와 유사하지만, 초기값을 가질 수 있습니다.
- count: Flow에서 발행된 값의 개수를 계산합니다.
- launchIn: 현재의 Coroutine scope 내에서 Flow를 실행시키고, 결과는 처리하지 않고 즉시 제어를 반환합니다. 주로 side-effect가 목적인 경우에 사용됩니다.
- onEach: 각 발행된 값에 대해 액션을 수행하고, 계속해서 다른 터미널 연산자를 호출할 수 있습니다.
- onCompletion: Flow가 완료될 때 호출되는 콜백을 정의합니다. 정상적인 완료 또는 예외 발생 시 모두 호출됩니다.
Flows are sequential
당연한 이야기이지만 Flow는 순차적으로 실행된다고 한다.
또한 수집은 터미널 연산자를 호출하는 코루틴내에서 수행되며 새로운 코루틴을 생성하는것은 아니다. (그것을 호출하는 코루틴을 이용)