RxJS로 옵저버블을 만드는 방법
RxJS로 옵저버블을 만드는 다양한 방법을 배워봅니다.
원문 링크입니다 : https://www.thisdot.co/blog/creating-observables-in-rxjs
Creating Observables in RxJS - This Dot Labs
Observables are the foundation of RxJS. Everything to do with RxJS revolves around Observables. In this article, we will look at the many different methods of…
www.thisdot.co
Observable은 RxJS의 기초입니다.
RxJS와 관련된 모든 것은 Observable을 중심으로 이루어집니다.
이 아티클에서는 RxJS에서 제공하는 Observable을 생성하는 다양한 방법을 살펴보겠습니다.
RxJS에서 Observable을 생성하는 두 가지 주요 방법이 있습니다.
Subject 및 Operator 입니다.
우리는 이 두 가지를 모두 살펴볼 것입니다!
Observable이 뭔가요?
Observable은 동기 또는 비동기로 여러 값을 Observer에 푸시하는 인수가 없는 함수와 같습니다.
const obs$ = Observable.create((observer) => {
observer.next(1);
observer.next(2);
observer.next(3);
setTimeout(() => observer.next(4), 1000);
});
console.log("before subscribe");
const observer = obs$.subscribe((v) => console.log("received: ", v));
console.log("after subscribe");
before subscribe
received: 1
received: 2
received: 3
after subscribe
received: 4
Observer가 값 푸시를 완료했음을 알릴 때까지 Observer는 값을 계속 수신합니다.
위의 예를 수정하면 실제로 작동하는 것을 볼 수 있습니다.
const obs$ = Observable.create((observer) => {
observer.next(1);
observer.next(2);
observer.complete();
observer.next(3);
setTimeout(() => observer.next(4), 1000);
});
console.log("before subscribe");
obs$.subscribe((v) => console.log("received: ", v));
console.log("after subscribe");
우리는 observer.complete()에 대한 호출을 추가했습니다.
Observer.next(2) 이후에 Observer가 값 푸시를 완료했음을 Observer에 알립니다.
결과를 봅시다.
before subscribe
received: 1
received: 2
after subscribe
Creating Observables with Subjects
Subject
const subject$ = new Subject();
const observerA = subject$.subscribe((v) => console.log("Observer A: ", v));
const observerB = subject$.subscribe((v) => console.log("Observer B: ", v));
subject$.next(1);
const observerC = subject$.subscribe((v) => console.log("Observer C: ", v))
subject$.next(2);
- Subject에게 값 1을 푸시하도록 지시합니다.
- 그런 다음 Subject에서 수신한 각 값을 기록하는 ObserverC를 만듭니다.
- 마지막으로 Subject에게 값 2를 푸시하도록 지시합니다.
Observer A: 1
Observer B: 1
Observer A: 2
Observer B: 2
Observer C: 2
ObserverA와 ObserverB는 둘 다 1을 받았지만 ObserverC는 2만 받은 것을 볼 수 있습니다.
기본적인 Subject의 Observer는 구독한 후에 푸시된 값만 수신한다는 점을 강조합니다!
BehaviorSubject
const behaviorSubject$ = new BehaviorSubject();
const observerA = behaviorSubject$.subscribe((v) => console.log("Observer A: ", v));
const observerB = behaviorSubject$.subscribe((v) => console.log("Observer B: ", v));
behaviorSubject$.next(1);
const observerC = behaviorSubject$.subscribe((v) => console.log("Observer C: ", v))
behaviorSubject$.next(2);
Observer A: 1
Observer B: 1
Observer C: 1
Observer A: 2
Observer B: 2
Observer C: 2
ReplaySubject
const replaySubject$ = new ReplaySubject(2); // 2 - number of values to store
const observerA = replaySubject$.subscribe((v) => console.log("Observer A: ", v));
replaySubject$.next(1);
replaySubject$.next(2);
replaySubject$.next(3);
const observerB = replaySubject$.subscribe((v) => console.log("Observer B: ", v))
replaySubject$.next(4);
이번에는 ReplaySubject가 4개의 값을 Observer에 푸시하도록 할 것입니다.
우리는 또한 그것이 방출한 두 개의 최신 값을 항상 저장해야 한다고 말합니다.
Observer A: 1
Observer A: 2
Observer A: 3
Observer B: 2
Observer B: 3
Observer A: 4
Observer B: 4
AsyncSubject
const asyncSubject$ = new AsyncSubject(2);
const observerA = asyncSubject$.subscribe((v) =>
console.log("Observer A: ", v)
);
asyncSubject$.next(1);
asyncSubject$.next(2);
const observerB = asyncSubject$.subscribe((v) =>
console.log("Observer B: ", v)
);
asyncSubject$.next(3);
asyncSubject$.complete();
const observerC = asyncSubject$.subscribe((v) =>
console.log("Observer C: ", v)
);
Observer A: 3
Observer B: 3
Observer C: 3
ObserverA는 값이 푸시되기 전에 구독했지만, 3개의 값중 마지막 값인 3만 수신했음을 알 수 있습니다.
ObserverC도 AsyncSubject가 완료(complete)된 후 구독했음에도, 즉시 값 3을 수신했음을 알 수 있습니다.
Operators로 Observable 만들기
여기에서 이러한 연산자 목록을 볼 수 있습니다.
http://reactivex.io/rxjs/manual/overview.html#creation-operators
ajax
const obs$ = ajax("https://api.github.com/users?per_page=2");
obs$.subscribe((v) => console.log("received: ", v.response));
received: (2) [Object, Object]
bindCallback
bindCallback을 사용하면 일반적으로 콜백 접근 방식을 사용하는 모든 함수를 Observable로 변환할 수 있습니다.
설명만으론 이해하기 상당히 어려울 수 있으므로 예를 들어 설명하겠습니다.
// Let's say we have a function that takes two numbers, multiplies them
// and passes the result to a callback function we manually provide to it
function multiplyNumbersThenCallback(x, y, callback) {
callback(x * y);
}
// We would normally use this function as shown below
multiplyNumbersThenCallback(3, 4, (value) =>
console.log("Value given to callback: ", value)
);
// However, with bindCallback, we can turn this function into
// a new function that takes the same arguments as the original
// function, but without the callback function
const multiplyNumbers = bindCallback(multiplyNumbersThenCallback);
// We call this function with the numbers we want to multiply
// and it returns to us an Observable that will only push
// the result of the multiplication when we subscribe to it
multiplyNumbers(3, 4).subscribe((value) =>
console.log("Value pushed by Observable: ", value)
);
bindCallback을 사용하면 Callback API를 사용하는 함수를 가져와
구독할 수 있는 Observable을 생성하는 반응적인 함수로 변환할 수 있습니다.
defer
const defferedObs$ = defer(() => of([1, 2, 3]));
const observerA = defferedObs$.subscribe((v) => console.log("Observer A: ", v));
const observerB = defferedObs$.subscribe((v) => console.log("Observer B: ", v));
출력은 다음과 같습니다.
Observer A: (3) [1, 2, 3]
Observer B: (3) [1, 2, 3]
let numOfObservers = 0;
const defferedObs$ = defer(() => {
if(numOfObservers === 0) {
numOfObservers++;
return of([1, 2, 3]);
}
return of([4,5,6])
});
const observerA = defferedObs$.subscribe((v) => console.log("Observer A: ", v));
const observerB = defferedObs$.subscribe((v) => console.log("Observer B: ", v));
첫 번째 Observer에게 [1, 2, 3]의 Observable을 제공하고 다른
모든 Observers [4, 5, 6]에 제공하도록 defer 객체를 변경했습니다.
이제 출력에서 다른 점을 볼 수 있습니다.
Observer A: (3) [1, 2, 3]
Observer B: (3) [4, 5, 6]
empty
const obs$ = empty();
obs$.subscribe((v) => console.log("received: ", v));
from
from은 강력한 연산자입니다.
거의 모든 것을 Observable로 변환하고
이러한 원천(source) 자체를 기반으로 지능적인 방식으로 이러한 소스의 값을 푸시합니다.
const obs$ = from([1,2,3]);
obs$.subscribe((v) => console.log("received: ", v));
received: 1
received: 2
received: 3
function* countToTen() {
let i = 0;
while(i < 11) {
yield i;
i++;
}
}
const obs$ = from(countToTen());
obs$.subscribe((v) => console.log("received: ", v));
fromEvent
fromEvent Operator는
웹 페이지의 모든 클릭과 같이 지정된 이벤트 타겟에서 발생한 지정된 유형의 모든 이벤트를 푸시하는 Observable을 생성합니다.
const obs$ = fromEvent(document, "click");
obs$.subscribe(() => console.log("received click!"));
received click!
received click!
fromEventPattern
addHandler 함수는 Observable이 구독될 때 호출되며
구독한 Observer는 addHandler 함수에 설정된 모든 이벤트를 수신합니다.
실제 코드를 보는 것보다 설명이 복잡할 수 있습니다.
페이지에서 발생하는 모든 클릭을 가져오려는 위의 예를 사용하겠습니다.
function addHandler(handler) {
document.addEventListener('click', handler)
}
function removeHandler(handler) {
document.removeEventListener('click', handler)
}
const obs$ = fromEventPattern(addHandler, removeHandler);
obs$.subscribe(() => console.log("received click!"));
received click!
received click!
generate
generater를 사용하면 멈출 때를 알려주는 조건(predicate)과 함께
전달한 인수를 기반으로 푸시할 값을 생성하는 Observable을 만들 수 있습니다.
const obs$ = generate(
1,
(x) => x < 11,
(x) => x++
)
obs$.subscribe((v) => console.log("received: ", v));
received: 0
received: 1
received: 2
received: 3
received: 4
received: 5
received: 6
received: 7
received: 8
received: 9
received: 10
interval
interval 연산자는 설정된 시간 간격으로 새 값을 푸시하는 Observable을 만듭니다.
아래 예제는 매초 새 값을 푸시하는 Observable을 만드는 방법을 보여줍니다.
const obs$ = interval(1000);
obs$.subscribe((v) => console.log("received: ", v));
received: 0
received: 1
received: 2
never
never 연산자는 새 값을 푸시하지 않고 오류가 발생하지 않으며 완료되지 않는 Observable을 만듭니다.
다른 Observable과 함께 테스트하거나 구성하는 데 유용할 수 있습니다.
const obs$ = never();
// This never logs anything as it never receives a value
obs$.subscribe((v) => console.log("received: ", v));
of
of 연산자는
제공한 인수와 동일한 순서로 값을 푸시한 다음 완료하는 Observable을 생성합니다.
from 연산자와 달리 배열의 모든 요소를 가져와서 각각 푸시하지는 않습니다.
대신 전체 배열을 하나의 값으로 푸시합니다.
const obs$ = of(1000, [1,2,4]);
obs$.subscribe((v) => console.log("received: ", v));
received: 1000
received: (3) [1, 2, 4]
range
range 연산자는 지정된 두 값 사이의 값을 순서대로 푸시하는 Observable을 만듭니다.
10까지 세는 함수를 range 연산자를 사용하여 생성할 수 있는 방법을 보겠습니다.
const obs$ = range(0, 10);
obs$.subscribe((v) => console.log("received: ", v));
received: 0
received: 1
received: 2
received: 3
received: 4
received: 5
received: 6
received: 7
received: 8
received: 9
received: 10
throwError
throwError 연산자는 값을 푸시하지 않고 즉시 오류 알림을 푸시하는 Observable을 생성합니다.
Observer가 Observable을 구독할 때 Observable이 던진 오류를 정상적으로 처리할 수 있습니다.
const obs$ = throwError(new Error("I've fallen over"));
obs$.subscribe(
(v) => console.log("received: ", v),
(e) => console.error(e)
);
Error: I've fallen over
timer
timer는 지정된 지연이 끝날 때까지 값을 푸시하지 않는 Observable을 만듭니다.
또한 초기 지연 후 각 간격에서 증가하는 값을 푸시하는 간격 시간을 알릴 수 있습니다.
const obs$ = timer(3000, 1000);
obs$.subscribe((v) => console.log("received: ", v));
received: 0
received: 1
received: 2
received: 3