Closed getogrand closed 5 years ago
공부하면서 이해가 안 가는 부분이 있네요.
ReactiveX 공식 홈 Introduction 문서에 아래와 같이 써있는데요.
It is sometimes called “functional reactive programming” but this is a misnomer. ReactiveX may be functional, and it may be reactive, but “functional reactive programming” is a different animal. One main point of difference is that functional reactive programming operates on values that change continuously over time, while ReactiveX operates on discrete values that are emitted over time. (See Conal Elliott’s work for more-precise information on functional reactive programming.)
continuously한 값의 변경을 여러번 다루는 것과 discrete한 값의 변경을 여러번 다루는 것이 어떻게 다른지 잘 이해가 안갑니다. 혹시 아시는 분 계실까요?
<button id="btn-increase">increase</button>
<p id="count">0</p>
<button id="btn-decrease">decrease</button>
<script src="https://npmcdn.com/@reactivex/rxjs@5.0.0-beta.8/dist/global/Rx.umd.js"></script>
<script>
let count = 0;
const updateCount = (count) => {
console.log(count);
document.getElementById("count").innerText = count
};
Rx.Observable.fromEvent(document.getElementById('btn-increase'), 'click')
.map(() => count++)
.subscribe((x) => updateCount(x));
Rx.Observable.fromEvent(document.getElementById('btn-decrease'), 'click')
.map(() => count--)
.subscribe((x) => updateCount(x));
</script>
import (
"bufio"
"fmt"
"os"
"github.com/reactivex/rxgo/handlers"
"github.com/reactivex/rxgo/observable"
)
func main() {
num := 0
reader := bufio.NewReader(os.Stdin)
onNext := handlers.NextFunc(func(item interface{}) {
if text, ok := item.(string); ok {
switch {
case text == "u\n":
num += 1
case text == "d\n":
num -= 1
default:
fmt.Println("Input has to be 'u' or 'd'")
return
}
}
fmt.Printf("Current num: %d\n", num)
})
for {
fmt.Print("type> ")
sub := observable.Start(func() interface{} {
text, err := reader.ReadString('\n')
if err != nil {
return err
}
return text
}).Subscribe(onNext)
<-sub
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="counterValue"
type="long"/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:autoSizeMaxTextSize="120sp"
android:autoSizeMinTextSize="20sp"
android:text="@{ String.valueOf(counterValue) }"
android:textSize="120sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/panel_barrier"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
tools:text="100"/>
<Button
android:id="@+id/counter_down"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/counter_down"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/counter_up"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.5"/>
<Button
android:id="@+id/counter_up"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/counter_up"
android:textSize="18sp"
app:layout_constraintStart_toEndOf="@+id/counter_down"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.5"/>
<androidx.constraintlayout.widget.Barrier
android:id="@+id/panel_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="counter_down,counter_up"
tools:layout_editor_absoluteY="731dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main).apply {
// 1. View.setOnClickListener
counterValue = 0
counterDown.setOnClickListener {
counterValue -= 1
}
counterUp.setOnClickListener {
counterValue += 1
}
// 2. RxAndroid
counterValue = 0
val minusPublishSubject: PublishSubject<Long> = PublishSubject.create<Long>()
counterDown.setOnClickListener {
minusPublishSubject.onNext(-1L)
}
val plusObservable: Observable<Long> = Observable.create<Long> { emitter ->
counterUp.setOnClickListener {
emitter.onNext(1L)
}
}.doOnDispose { counterUp.setOnClickListener(null) }
disposable = Observable.merge(minusPublishSubject, plusObservable)
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ counterValue += it }, Throwable::printStackTrace, {})
}
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>RxJS</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.4.0/rxjs.umd.min.js"></script>
</head>
<body>
<span id="currNum">0</span>
<button id="increaseBtn">Increase</button>
<button id="decreaseBtn">Decrease</button>
<script>
document.addEventListener("DOMContentLoaded", () => {
rxjs.fromEvent(document.getElementById("increaseBtn"), "click").subscribe(() => {
const currNumEle = document.getElementById("currNum");
const currNum = parseInt(currNumEle.innerText);
currNumEle.innerText = currNum + 1;
});
rxjs.fromEvent(document.getElementById("decreaseBtn"), "click").subscribe(() => {
const currNumEle = document.getElementById("currNum");
const currNum = parseInt(currNumEle.innerText);
currNumEle.innerText = currNum - 1;
});
});
</script>
</body>
</html>
Regex Utility WebSites
Visites ReactiveX Sites / Documents
Remote & Local Caching
fromRemoteCache =
Api.fetchItems() // Single for most of HTTP API Call
.subscribeOn(Schedulers.io())
.toObservable() // Single => Observable
.publish() // Observable => PublishSubject
.refCount(); // Dispose if refCount goes 0
fromLocalCache =
Observable.fromCallable(LocalStorage::loadItems)
.subscribeOn(Schedulers.io())
.takeUntil(fromRemoteCache) // Stream halts if [fromRemoteCache] emits any item
return Observable.merge(fromRemoteCache, fromLocalCache);
// .share() is equal to .publish().refCount() for multicast
// fromRemoteCache.subscribe(Observer1) // refCount: 1 (Initialize data source)
// fromRemoteCache.subscribe(Observer2) // refCount: 2 (Use initialized data)
// fromRemoteCache.subscribe(Observer3) // refCount: 3 (Use initialized data)
// fromRemoteCache.unSubscribe(Observer1) // refCount: 2
// fromRemoteCache.unSubscribe(Observer2) // refCount: 1
// fromRemoteCache.unSubscribe(Observer3) // refCount: 0 (dispose observable defined in .doOnDispose{...})
// fromRemoteCache.subscribe(Observer1) // refCount: 1 (Initialize data source)
// fromRemoteCache.subscribe(Observer2) // refCount: 2 (Use initialized data)
// fromRemoteCache.unSubscribe(Observer1) // refCount: 1
// fromRemoteCache.unSubscribe(Observer2) // refCount: 0 (dispose observable defined in .doOnDispose{...})
현재 숫자와 up, down 버튼이 있는 간단한 카운터 어플리케이션을 ReactiveX를 이용해 만들어 봅시다.