Callback and Reactive Streams
designpattern kotlin
Problems
Use-case on Android
Client side 에서는 View 를 Rendering 하는 과정에서 Main Thread 가 Block 되지 않아야 한다.
fun renderSomeView() {
val data = getSomePageScripting() // API Call
val result = process(data) // Processing
view.show(result)
}
위 코드의 경우에는 Process 를 하는 과정에서 Main Thread 가 Blocking 될 수 있는 문제가 있다.
Thread Context Switching
fun renderSomeView() {
thread {
val data = getSomePageScripting() // API Call
val result = process(data) // Processing
view.show(result)
}
}
Thread Context Switching 를 적용한 위 코드는 아래와 같은 문제가 있을 수 있다.
- Multi-Thread 프로그래밍을 할때 항상 다른 Thread 를 Interrupt 할 수 있는 수단이 존재해야 하는데 존재하지 않아 Memory Leak 을 유발할 수 있다.
- 잦은 Context Switching 은 관리하는 것이 어렵다.
이러한 문제를 Callback Pattern 을 이용해 해결할 수 있다.
Callback
Callback Pattern 은 Process 가 종료됐을때 실행하길 원하는 Function 을 넘겨준 뒤, 해당 Process 가 종료되면 넘겨받은 Function 을 실행하게끔 하는 것을 의미한다.
fun process(callback: () -> Unit) = thread {
Thread.sleep(1000)
// do some process
callback.invoke() // invoke callback
}
fun main() {
val callbackFn = { println(", World!") }
process(callbackFn)
println("Hello")
Thread.sleep(2000) // join
}
위와 같이 process 내에서 도는 함수가 다 끝난뒤 넘겨 받은 callback 을 invoke() 하게 된다.
Callback Hell
Callback Pattern 을 계속해서 사용하다 보면 아래와 같은 코드를 'Callback Hell' 혹은 '아도겐 코드' 라고 부른다.
fun doSome() {
f1(context) {
f2 (context) {
f3 (context) {
doSomething()
}
}
}
}
Reactive Streams
The purpose of Reactive Streams is to provide a standard for asynchronous stream processing with non-blocking backpressure.
JVM 진영에서는 Reactive Streams 를 활용하여 Callback Pattern 처럼 Depth 가 깊어지지 않고, 하나의 Streams 안에서 해야할 일을 명시하도록 제공하고 있다.
fun renderSomeView() {
getSomePageScripting()
.subScribeOn(Scheduler.io())
.map (pprocess(it))
.subscribe { view.show(it) }
.doOnError { e -> println(e.message) }
}