오늘도 더 나은 코드를 작성하였습니까?

Flow를 사용한 동시 실행 제어 본문

Coroutine/coroutineFlow

Flow를 사용한 동시 실행 제어

hik14 2021. 5. 31. 18:18

onClick에서 호출된 핸들러에서 네트워크 호출을 실행하는 로직이 삭제되고 growZone에서 구동됩니다. 그러면 단일 소스 저장소(SSOT)를 만들어 코드 중복을 방지할 수 있습니다. 어떤 코드도 캐시 새로고침 없이 필터를 변경할 수 없습니다.

 

private val growZoneFlow = MutableStateFlow<GrowZone>(NoGrowZone)


init {
   clearGrowZoneNumber()

   growZoneFlow.mapLatest { growZone ->
           _spinner.value = true
           if (growZone == NoGrowZone) {
               plantRepository.tryUpdateRecentPlantsCache()
           } else {
               plantRepository.tryUpdateRecentPlantsForGrowZoneCache(growZone)
           }
       }
       .onEach {  _spinner.value = false }
       .catch { throwable ->  _snackbar.value = throwable.message  }
       .launchIn(viewModelScope)
}

 

1. launchIn

 growZoneFlow
     // ...
         .launchIn(viewModelScope)

 

launchIn 연산자를 사용하여 ViewModel 내에서 flow을 collecting합니다.

 

연산자 launchIn -  새 코루틴을 만들고 흐름에서 모든 값을 수집합니다.

 

제공된 CoroutineScope(이 경우 viewModelScope)에서 실행됩니다. 이렇게 되면 이 ViewModel이 삭제되면 수집이 취소되므로 유용합니다.  다른 연산자가 제공되지 않으면 그리 많은 작업을 하지 못합니다.

하지만 Flow에서는 모든 연산자에 정지 람다를 제공하므로 모든 값을 기반으로 비동기 작업을 쉽게 실행할 수 있습니다.

 

 

Flow를 사용하면 ViewModel, Repository 또는 필요에 따라 다른 데이터 영역에서 데이터를 수집하는 것이 자연스럽습니다.

Flow는 UI에 연결되어 있지 않으므로 flow을 collect 처리하기 위해 UI 관찰자가 필요하지 않습니다.

 

이 점은 항상 UI 관찰자의 실행이 필요한 LiveData와 크게 다른 부분입니다.

 ViewModel에서는 적절한 관찰 수명 주기가 없으므로 LiveData에 관한 observe를 시도하는 것은 좋지 않습니다.(viewModel 내에서 LiveData를 관찰해서는 안됨.)

 

2. mapLatest

.mapLatest { growZone ->
    _spinner.value = true
    if (growZone == NoGrowZone) {
        plantRepository.tryUpdateRecentPlantsCache()
    } else {
        plantRepository.tryUpdateRecentPlantsForGrowZoneCache(growZone)
    }
}

여기서 놀라운 일이 일어납니다. mapLatest는 각 값의 매핑 함수를 적용합니다. 그러나 일반 map과 달리 매핑 transform을 호출할 때마다 새 코루틴이 실행됩니다. 그런 다음, 이전 코루틴이 완료되기 전에 growZoneChannel에서 새 값을 내보내면 이전 코루틴을 취소한 후에 새 코루틴을 시작합니다.

 

mapLatest를 사용하여 동시 실행을 제어할 수 있습니다. 취소/다시 시작 로직을 직접 작성하는 대신 flow transform에서 처리할 수 있습니다. 이 코드는 동일한 취소 로직을 직접 작성하는 경우에 비해 복잡성과 작성할 코드 양을 크게 줄여 줍니다.

 

3.

.onEach {  _spinner.value = false }
.catch { throwable -> _snackbar.value = throwable.message }

onEach-  그 이전의 flow에서 값을 내보낼 때마다 호출됩니다. 여기서는 처리가 완료된 후 스피너를 초기화하는 데 사용합니다.

 

catch 연산자는 흐름에서 그 이전에 발생한 예외를 캡처합니다. 오류 상태와 같은 새 값을 흐름에 내보내거나, 예외를 흐름에 다시 반환하거나, 여기서와 같이 작업을 실행할 수 있습니다. 오류가 발생하면 오류 메시지를 표시하도록 _snackbar에 지시합니다.

'Coroutine > coroutineFlow' 카테고리의 다른 글

flow 중간 연산자 (Zip, Combine)  (0) 2021.06.04
Stateflow LiveData  (0) 2021.06.02
Flow를 사용한 스타일 혼합  (0) 2021.05.31
단일값을 보내는 flow 와 flow의 결합.  (0) 2021.05.31
flow을 사용한 비동기 작업  (0) 2021.05.31