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

flow 중간 연산자(map, transFrom, flatte) 본문

Coroutine/coroutineFlow

flow 중간 연산자(map, transFrom, flatte)

hik14 2021. 6. 4. 20:03

map

- 기존 코틀린 collection 확장함수인 map 동일하다.

- flow 값을 변환 시켜 보낸다.

- 람다로 변환함수만 넘기면 알아서 전부 emit된다. 

suspend fun main() {

    val intFlow = (1..10).asFlow().map {
        it*it
    }

    intFlow.collect { int ->
        println(int)
    }
}

 

transFrom

- map과 비슷하지만 단순 값의 변환 보다는 타입을 명식적으로 기재해 타입을 변환할때 사용한다.

- 타입을 변환후 emit()사용하여 보낸다. 

suspend fun main() {

    val intFlow = (1..10).asFlow().transform<Int, String> { num ->
        val str = num.toString()+"번"
        emit(str)
    }

    intFlow.collect { str ->
        println(str)
    }
}

 

flatMapConcat

- 중첩된 collection의 flatten 확장함수와 비슷하게 중첩된 flow를 flow로 만든다.

- 중첩 된 flow을 인터리빙하지 않고 순차적으로 지정된 flow로 flow을 하나의 flow로  만듭니다.

- 이 메서드는 개념적으로 flattenMerge (concurrency = 1)와 동일하지만 속도가 더 빠릅니다

suspend fun main() {

    val charFlow = ('a'..'d').asFlow().onEach { delay(100) }
        .onEach {
            delay(100)
        }
        .flatMapConcat { char ->
            makeFlow(char)
        }

    charFlow.collect { i ->
        print(i)
    }
}

fun makeFlow(c: Char): Flow<String> = flow {
    val start = c.toInt()
    val end = start + 5

    emit("$c: ")
    delay(500)

    val str = (start..end).map { it.toChar() }.toString()
    emit(str)
    println("")
}

a b c d 를  순차적으로 내보내는 flow에서

각각 알파벳을 시작으로 뒤로 5개를 내보내는 다른 플로우를 생성한다.

 

여기서 flatMapConcat을 이용하여 flow<flow<Char>>의 형태의 flow를 

단일값인 char로 내보낸다.  

 

만약 그냥 맵을 사용한다면, 아래와 같이 그냥 flow<String>의 주소값이 찍힌다. 

suspend fun main() {

    val charFlow = ('a'..'d').asFlow().onEach { delay(100) }
        .onEach {
            delay(100)
        }
        .map { char ->
            makeFlow(char)
        }

    charFlow.collect { i ->
        print(i)
    }
}

fun makeFlow(c: Char): Flow<String> = flow {
    val start = c.toInt()
    val end = start + 5

    emit("$c: ")
    delay(500)

    val str = (start..end).map { it.toChar() }.toString()
    emit(str)
    println("")
}

 

flatMapMerge

- 동시에 emit 가능한 값들을 emit 시키고 들어오는 모든 값들을 하나의 flow로 병합한다.
- 즉 makeflow 첫번째 emit("$c: ")이 실행후 0.5초의 딜레이 동안 아래 emit(str)이 연산되기전에 먼저 내보낼수 있는 값이 있다면 먼저 내보내고 통합한다. 

suspend fun main() {

    val charFlow = ('a'..'d').asFlow().onEach { delay(100) }
        .onEach {
            delay(100)
        }
        .flatMapMerge { char ->
            makeFlow(char)
        }

    charFlow.collect { i ->
        print(i)
    }
}

fun makeFlow(c: Char): Flow<String> = flow {
    val start = c.toInt()
    val end = start + 5

    emit("$c: ")
    delay(500)

    val str = (start..end).map { it.toChar() }.toString()
    emit(str)
    println("")
}

faltMapConcat은  a: --> [a, b, c, d, e, f] 이렇게 내보냈을텐데, faltMapMerge는  [a, b, c, d, e, f] 를 내보내기전에 b, c를 먼저 내보낼수 있다고 판단되면 먼저 값을 내보낸다.

flatMapLatest

- flow에서 emit발생시 이전에 대기중이거나 동작중인 작업이 진행중일때 더 최신값을 emit 할수 있다면 기존의 작업을 중단및 취소후 최신값을 반환한다. 

suspend fun main() {

    val charFlow = ('a'..'d').asFlow().onEach { delay(100) }
        .onEach {
            delay(100)
        }
        .flatMapLatest { char ->
            makeFlow(char)
        }

    charFlow.collect { i ->
        print(i)
    }
}

fun makeFlow(c: Char): Flow<String> = flow {
    val start = c.toInt()
    val end = start + 5

    emit("$c: ")
    delay(500)

    val str = (start..end).map { it.toChar() }.toString()
    emit(str)
    println("")
}

  [a, b, c, d, e, f] 를 내보내는 작업을 하던중 지속적인 새로운 최신 emit 값을 내보낼수있기에 전부 취소 되고 다소 시간이 덜걸리는

emit("$c: ") 실행되고 마지막 [d, e, f, g,h, i]만 실행된다.