=================
== The Archive ==
=================

[Kotlin Coroutines] 22장. 플로우 생존주기 함수

Introduction

onEach

1
2
3
4
5
suspend fun main() {
    flowOf(1, 2, 3, 4)
        .onEach { print(it) }
        .collect {} // 1234
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
suspend fun main() {
    flowOf(1, 2)
        .onEach { delay(1000) }
        .collect { println(it) }
}

// (1 sec)
// 1
// (1 sec)
// 2

onStart

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
suspend fun main() {
    flowOf(1, 2)
        .onEach { delay(1000) }
        .onStart { println("Before") }
        .collect { println(it) }
}

// Before
// (1 sec)
// 1
// (1 sec)
// 2
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
suspend fun main() {
    flowOf(1, 2)
        .onEach { delay(1000) }
        .onStart { emit(0) }
        .collect { println(it) }
}

// 0
// (1 sec)
// 1
// (1 sec)
// 2

onCompletion

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
suspend fun main() =
    coroutineScope {
        flowOf(1, 2)
            .onEach { delay(1000) }
            .onCompletion { println("Completed") }
            .collect { println(it) }
    }

// (1 sec)
// 1
// (1 sec)
// 2
// Completed

onEmpty

1
2
3
4
5
6
7
8
9
suspend fun main() =
    coroutineScope {
        flow<List<Int>> { delay(1000) }
            .onEmpty { emit(emptyList()) }
            .collect { println(it) }
    }

// (1 sec)
// []

catch

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
private class MyError : Throwable("My error")

private val flowInCatch1: Flow<Int> =
    flow {
        emit(1)
        emit(2)
        throw MyError()
    }

suspend fun main() {
    flowInCatch1
        .onEach { println("Got $it") }
        .catch { println("Caught $it") }
        .collect { println("Collected $it") }
}

// Got 1
// Collected 1
// Got 2
// Collected 2
// Caught MyError: My error
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
private class MyErrorInCatch2 : Throwable("My error")

private val flowInCatch2 =
    flow {
        emit("Message1")
        throw MyErrorInCatch2()
    }

suspend fun main() {
    flowInCatch2
        .catch { emit("Error") }
        .collect { println("Collected $it") }
}

// Collected Message1
// Collected Error

잡히지 않은 예외

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
private class MyErrorInUncaughtExceptions1 : Throwable("My error")

private val flowInUncaughtExceptions1 =
    flow {
        emit("Message1")
        throw MyErrorInUncaughtExceptions1()
    }

suspend fun main() {
    try {
        flowInUncaughtExceptions1.collect { println("Collected $it") }
    } catch (e: MyErrorInUncaughtExceptions1) {
        println("Caught")
    }
}

// Collected Message1
// Caught
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
private class MyErrorInUncaughtExceptions2 : Throwable("My error")

private val flowInUncaughtExceptions2 =
    flow {
        emit("Message1")
        emit("Message2")
    }

suspend fun main() {
    flowInUncaughtExceptions2
        .onStart { println("Before") }
        .catch { println("Caught $it") }
        .collect { throw MyErrorInUncaughtExceptions2() }
}

// Before
// Exception in thread "..." MyError: My error
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
private class MyErrorInUncaughtExceptions3 : Throwable("My error")

private val flowInUncaughtExceptions3 =
    flow {
        emit("Message1")
        emit("Message2")
    }

suspend fun main() {
    flowInUncaughtExceptions3
        .onStart { println("Before") }
        .onEach { throw MyErrorInUncaughtExceptions3() }
        .catch { println("Caught $it") }
        .collect {}
}

// Before
// Caught MyError: My error

flowOn

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private fun usersFlow(): Flow<String> =
    flow {
        repeat(2) {
            val ctx = currentCoroutineContext()
            val name = ctx[CoroutineName]?.name
            emit("User$it in $name")
        }
    }

suspend fun main() {
    val users = usersFlow()
    withContext(CoroutineName("Name1")) {
        users.collect { println(it) }
    }
    withContext(CoroutineName("Name2")) {
        users.collect { println(it) }
    }
}

// User0 in Name1
// User1 in Name1
// User0 in Name2
// User1 in Name2
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private suspend fun present(
    place: String,
    message: String,
) {
    val ctx = coroutineContext
    val name = ctx[CoroutineName]?.name
    println("[$name] $message on $place")
}

private fun messagesFlow(): Flow<String> =
    flow {
        present("flow builder", "Message")
        emit("Message")
    }

suspend fun main() {
    val users = messagesFlow()
    withContext(CoroutineName("Name1")) {
        users
            .flowOn(CoroutineName("Name3"))
            .onEach { present("onEach", it) }
            .flowOn(CoroutineName("Name2"))
            .collect { present("collect", it) }
    }
}

// [Name3] Message on flow builder
// [Name2] Message on onEach
// [Name1] Message on collect

launchIn

1
2
3
public fun <T> Flow<T>.launchIn(scope: CoroutineScope): Job = scope.launch {
    collect() // tail-call
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
suspend fun main(): Unit =
    coroutineScope {
        flowOf("User1", "User2")
            .onStart { println("Users:") }
            .onEach { println(it) }
            .launchIn(this)
    }

// Users:
// User1
// User2

요약