Combining Observables
둘 이상의 Observable 스트림을 하나로 묶어 새로운 Observable 시퀸스를 생성하는 과정을 의미한다.
예를 들어 Transforming Observable 파트에서 학습했던 flatMap 연산자는 변환함수를 적용하여 생성한 새로운 Observable들의 이벤트를 하나의 스트림에 추가하여 단일 Observable 스트림으로 결합하는 작업을 수행했다. 이 때, 결합하는 작업을 수행하는 것이 Combining Observable의 일종이라고 볼 수 있다.
결합 연산자의 종류에는 여러가지가 있지만,
merge, combineLatest, zip, switchLatest, startWith, concat 에 대해서 학습해보고자 한다.
1. Merge
merge 연산자는 여러 Observable 스트림을 하나로 병합하는 역할을 수행한다.
병합된 모든 Observable들은 하나의 스트림으로 동작하게 된다.
merge 사용예시
import RxSwift
let disposeBag = DisposeBag()
// 1초마다 방출하는 타이머 Observable
let timer1 = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
.map { "Timer 1: \($0)" }
// 2초마다 방출하는 타이머 Observable
let timer2 = Observable<Int>.interval(.seconds(2), scheduler: MainScheduler.instance)
.map { "Timer 2: \($0)" }
// 두 타이머 Observable을 병합
let mergedTimers = Observable.of(timer1, timer2).merge()
// 병합된 결과를 구독하고 출력
mergedTimers
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
//결과
//Timer 1: 0
//Timer 2: 0
//Timer 1: 1
//Timer 1: 2
//Timer 2: 1
...
먼저 'timer1', 'timer2' 두 개의 interval 옵저버블을 생성한다.
'timer1' 은 1초마다, 'timer2' 는 2초마다 값을 방출한다.
이 두개의 옵저버블을 of 연산자의 파라미터로 전달하면,
of 를 통해 생성된 옵저버블은 파라미터로 전달된 각 옵저버블을 하나씩 순서대로 방출한다.
하지만 여기서 merge 연산자를 사용하여 방출되는 두 Observable을 병합하면,
두 timer 옵저버블에서 방출하는 값이 하나의 스트림에서 방출된다.
merge 정의
extension ObservableType where Element: ObservableConvertibleType { ... }
merge는 ObservableType 프로토콜의 extension 메소드로 정의되어 있다.
여기서 'Element' 는 'ObservableType' 이 방출하는 요소의 타입을 나타낸다.
이 'Element' 는 'ObservableConvertibleType' 프로토콜을 준수해야 하는데,
'ObservableConvertibleType' 프로토콜은 어떤 타입이 'Observable' 로 변환될 수 있어야 함을 나타낸다.
(merge가 Observable을 결합하는 기능이기 때문에 merge를 사용하려는 Observable이 방출하는 요소가 Observable 형태여야 한다는 것 같음...)
그리고 merge의 정의는 2가지 형태가 있다.
첫 번째 형태의 merge
public func merge() -> Observable<Element.Element> {
Merge(source: self.asObservable())
}
현재 'OvservableType' 의 요소로부터 새로운 'Observable' 스트림을 병합하여 생성한다.
요소가 'ObservableConvertibleType' 프로토콜을 준수하므로 각 요소를 'Observable'로 변환하고, 이를 병합한다.
위의 사용예시가 바로 이 첫 번째 형태의 merge 를 사용한 예시.
두 번째 형태의 merge
public func merge(maxConcurrent: Int)
-> Observable<Element.Element> {
MergeLimited(source: self.asObservable(), maxConcurrent: maxConcurrent)
}
두 번째 형태의 'merge' 는 동시에 구독할 수 있는 내부 옵저버블의 수를 제한한다.
매개변수 'maxConcurrent' 를 사용하여 동시 구독의 최대 수를 지정할 수 있다.
쉽게 이해하기 위해서 두 번째 형태의 merge를 사용한 예시를 보자.
let observable1 = Observable.of(1, 2, 3)
let observable2 = Observable.of(4, 5, 6)
let observable3 = Observable.of(7, 8, 9)
let mergedObservable = Observable.of(observable1, observable2, observable3)
.merge(maxConcurrent: 2)
mergedObservable.subscribe(onNext: {
print($0)
}).disposed(by: disposeBag)
이렇게 'maxConcurrent' 가 2로 설정된 경우,
'observable1' 과 'observable2' 만 동시에 구독되고 병합된다.
그리고 만약 두 개의 옵저버블 중 하나의 옵저버블이 이벤트를 모두 방출하면,
대기 중이었던 'observable3' 이 구독되고 병합되어 방출된다.
두 번째 형태의 merge는 병합되는 옵저버블의 수가 많고,
모든 옵저버블이 동시에 활성화되면 리소스가 과도하게 사용될 수 있는 상황에서 유용하다.
동시에 처리되는 옵저버블의 수를 제어함으로써 리소스를 효율적으로 관리할 수 있다.
merge 의 한계점? 주의할 점?
1. 순서 보장
merge는 병합되는 Observable 스트림의 이벤트 순서를 보장하지 않는다.
위의 사용예시처럼 of 연산자의 매개변수로 가장 먼저 들어간 옵저버블이라고 해도,
결합되어 방출되는 결과를 보면, 들어간 순서대로 이벤트가 방출되지 않는다.
2. 에러 처리
병합되는 각 Observable 중 하나에서 발생한 에러는 병합된 Observable 스트림으로 전파된다.
이로 인해 하나의 에러가 전체 스트림을 중단시킬 수 있기 때문에, 적절한 에러 처리를 고려해야 한다.
3. 리소스 사용
두 번째 형태의 merge에서도 얘기했지만, 많은 수의 Observable을 병합하면,
동시에 많은 수의 작업이 수행되어 리소스 사용량이 증가할 수 있다.
따라서 필요한 만큼의 병합만 수행하고, 필요한 경우 최대 수를 제한하는 것이 효율적이다.
2. CombineLatest
두 개 이상의 Observable 최신 이벤트를 결합하여 새로운 이벤트를 생성하는데 사용된다.
위 그림에서 볼 수 있듯이 두 Observable이 방출하는 각 이벤트 중 마지막으로 방출된 이벤트를 결합하여 전달한다.
combineLatest 사용예시
let firstObservable = Observable.of(1, 2, 3)
let secondObservable = Observable.of("A", "B", "C")
let combined = Observable.combineLatest(firstObservable, secondObservable) { (first, second) -> String in
return "결합: \(first) \(second)"
}
let subscription = combined.subscribe(onNext: { value in
print(value)
})
//결과
//결합: 1 A
//결합: 2 B
//결합: 3 C
'combineLatest' 메서드는 두 개의 옵저버블을 매개변수로 받는다.
그리고 마지막으로 받는 매개변수는 트레일링 클로저를 통해 두 옵저버블에서 방출하는 요소를 가지고 어떤 작업을 할 지 결정한다. 클로저의 매개변수 first, second가 바로 두 옵저버블에서 방출되는 각 항목이다.
예시에서는 방출되는 두 요소인 Int와 String을 결합하여 새로운 String을 생성한다.
또한 예시에서는 두 옵저버블이 동시에 이벤트를 방출하는 작업을 수행했는데,
만약 동시에 이벤트가 방출되지 않았을 경우, 하나의 이벤트가 방출되어도 다른 나머지 하나의 최신 이벤트 요소와 결합하여 방출된다. 따라서 위의 결과가 꼭 3개로 딱 맞아 떨어지지 않을 수 있다는 것이다.
또한 지금까지 알아본 'combineLatest' 는 2개의 옵저버블만을 다루는 것이였는데,
2개만이 아니라 2개 이상의 옵저버블도 결합할 수 있다.
combineLatest 정의
extension ObservableType {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
- seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func combineLatest<O1: ObservableType, O2: ObservableType>
(_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.Element, O2.Element) throws -> Element)
-> Observable<Element> {
return CombineLatest2(
source1: source1.asObservable(), source2: source2.asObservable(),
resultSelector: resultSelector
)
}
}
'01' 과 '02' 는 각각 다른 Observable 타입을 나타낸다.
그리고 'source1' 과 'source2' 이 두 개의 매개변수를 받으며, 이 매개변수는 서로 결합된다.
마지막 매개변수 'resultSelector' 는 두 옵저버블에서 방출되는 최신 값을 매개변수로 받아서
원하는 형태로 결합하여 방출할 값을 반환한다.
'CombineLatest2' 클래스의 인스턴스를 생성하여 매개변수로 받아온 인자들을 전달해준다.
combineLatest 유용함
-동시 데이터 감시
여러 옵저버블 중 하나의 스트림에서 이벤트가 방출될 때마다 새로운 값을 도출해야 하는 경우 유용하다.
예를 들어, 상품의 수량 선택과 배송 옵션 선택이 있다고 가정하자.
유저가 수량을 선택할 때마다, 선택한 수량에 따라서 상품의 가격이 변동되어야 하고,
유저가 배송 옵션을 선택할 때마다, 배송 옵션에 따라 배송비가 달라진다.
이 두 개의 선택지를 combineLatest로 결합하면,
두 선택 사항 중 하나라도 변경되면, 즉시 반응하여 새로운 값을 생성할 수 있다.
그리고, 가장 최신의 값을 저장하고 있기 때문에 하나가 변경되어도 다른 하나는 변경할 문제가 없다.
3. Zip
여러 개의 Observable을 결합하여 각 Observable이 동일한 인덱스에서 방출하는 요소들을 결합하여 방출한다.
zip 사용예시
let observable1 = Observable.from([1, 2, 3])
let observable2 = Observable.from(["A", "B", "C"])
Observable.zip(observable1, observable2) { number, letter in
return "\(number): \(letter)"
}
.subscribe(onNext: { value in
print(value)
})
//결과
//1: A
//2: B
//3: C
각 from 옵저버블에서 방출하는 동일한 인덱스의 이벤트를 결합하여 방출한다.
zip 은 각 Observable에서 동일한 인덱스의 요소가 방출될 때까지 기다린다.
따라서 Observable 중 하나가 다른 것보다 더 빠르게 값을 방출해도, 더 느린 Observable의 값이 방출될 때까지 기다린다.
예시에서는 두 Observable이 방출하는 요소가 동일했는데,
만일 하나의 Observable이 다른 것보다 더 많은 값을 방출한다면, 다른 Observable의 남은 요소들은 무시될 수 있다.
따라서 모든 Observable이 같은 개수의 요소를 방출하는 것이 zip 을 효율적으로 사용하는 것.
zip의 정의
extension ObservableType {
/**
Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
- seealso: [zip operator on reactivex.io](http://reactivex.io/documentation/operators/zip.html)
- parameter resultSelector: Function to invoke for each series of elements at corresponding indexes in the sources.
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
*/
public static func zip<O1: ObservableType, O2: ObservableType>
(_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.Element, O2.Element) throws -> Element)
-> Observable<Element> {
return Zip2(
source1: source1.asObservable(), source2: source2.asObservable(),
resultSelector: resultSelector
)
}
}
zip의 정의도 combineLatest 정의와 유사하다.
'01' 과 '02' 는 각각 다른 Observable 타입을 나타낸다.
그리고 'source1' 과 'source2' 이 두 개의 매개변수를 받으며, 이 매개변수는 서로 결합된다.
마지막 매개변수 'resultSelector' 는 두 옵저버블에서 방출되는 최신 값을 매개변수로 받아서
원하는 형태로 결합하여 방출할 값을 반환한다.
그리고 'Zip2', 새롭게 만들어지는 Observable은 'resultSelector'의 반환 값을 이벤트의 요소로 방출한다.
zip의 유용함
-순차적인 데이터 가져오기
순서가 중요한 데이터 가져오기 작업에서 zip이 유용하게 사용될 수 있다.
예를 들어, 1. 유저의 정보를 가져오고, 2. 가져온 정보를 이용하여 유저의 주문 내역을 가져와는 상황을 가정해보자.
2번 작업은 1번 작업의 결과에 의존하기 때문에 이 두 작업은 순차적인 작업이어야 한다.
zip은 하나의 Observable에서 이벤트를 방출해도, 나머지 Observable이 이벤트 방출을 할 때까지 기다렸다가 결합을 진행하기 때문에 두 API 호출을 결합하여 원하는 작업을 수행할 수 있다.
zip의 주의할 점
-위에서도 설명했듯이 zip은 모든 Observable이 이벤트를 방출할 때까지 기다렸다가 결합 작업을 수행하기 때문에
대기 시간이 길어질 수 있다. 또한 방출 요소가 더 많은 Observable은 동일한 인덱스에 맞춰 요소가 방출되고, 나머지는 무시될 수 있기 때문에 주의해야 한다.
4. SwitchLatest
하나의 Observable이 방출하는 요소가 Observable일 때,
방출되는 Observable들 중 가장 최근에 방출된 Observable만 구독한다.
예를 들어, 여러 버튼 중 하나를 선택할 때마다 해당 버튼과 관련된 데이터를 로드해야 하는 상황이 있다고 하자.
각 버튼은 자신과 관련된 데이터를 로드하는 Observable을 가지고 있다.
이 여러 Observable에 switchLatest를 사용하면,
유저가 버튼을 선택할 때마다 이전에 눌렀던 버튼의 데이터 로드를 취소하고,
새로 선택된 버튼의 데이터 로드를 시작할 수 있다.
switchLatest 사용예시
먼저 상황을 가정해보면, 화면에 3개의 버튼이 있고, 각 버튼을 누를 때마다 다른 데이터 스트림을 구독하는 상황이다.
버튼 1을 누르면 데이터 스트림 1을, 버튼 2를 누르면 데이터 스트림 2를 구독하는 방식이다.
let disposeBag = DisposeBag()
// 세 개의 버튼 정의
let button1: UIButton = UIButton()
let button2: UIButton = UIButton()
let button3: UIButton = UIButton()
// 세 개의 옵저버블 정의
let stream1: Observable<String> = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance).map { "Stream 1: \($0)" }
let stream2: Observable<String> = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance).map { "Stream 2: \($0)" }
let stream3: Observable<String> = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance).map { "Stream 3: \($0)" }
// 각 버튼을 클릭할 때마다 해당 스트림을 방출하는 Observable 생성
let buttonStream = Observable.merge(
button1.rx.tap.map { stream1 },
button2.rx.tap.map { stream2 },
button3.rx.tap.map { stream3 }
)
// switchLatest를 사용하여 현재 선택된 버튼에 해당하는 스트림만 구독
let switchedStream = buttonStream.switchLatest()
// switchedStream 구독하여 결과 출력
switchedStream
.subscribe(onNext: { value in
print(value) // 버튼을 클릭할 때마다 해당 스트림의 값을 출력
})
.disposed(by: disposeBag)
3개의 옵저버블은 interval을 통해 1초마다 1씩 증가하는 정수를 요소로 방출하는데,
이 요소를 map 연산자를 통해 문자열로 내보낸다.
다음 'button.rx.tap' 은 유저가 버튼을 클릭할 때마다 이벤트를 방출할 수 있는 Observable을 생성하는 것이다.
이 Observable을 map 연산자를 사용하여 특정 스트림으로 변환한다.
이 때, 버튼을 클릭하면 각 스트림(stream1, stream2, stream3)이 이벤트를 방출한다.
그리고 merge를 통해 세 개의 버튼 클릭 이벤트를 하나의 스트림으로 결합한다.
다음 switchLatest를 사용하여 클릭된 버튼에 해당하는 스트림만 구독하도록 한다.
새로운 버튼이 클릭되면, 현재 구독 중인 스트림에서 새로 선택된 스트림으로 전환된다.
이를 실행하면,
버튼 1을 클릭했을 경우, 매초마다 'Stream1: 0', 'Stream1: 1' ... 를 출력한다.
버튼 2를 클릭했을 경우, 버튼 1을 통한 'stream1' 옵저버블의 구독을 취소하고, 'stream2' 가 구독되어 이벤트를 방출한다.
버튼 3를 클릭했을 경우, 버튼 2를 통한 'stream2' 옵저버블의 구독을 취소하고, 'stream3' 이 구독되어 이벤트를 방출한다.
(다시 버튼 1을 클릭했을 경우에는 다시 구독을 시작하는 것이기 때문에 'Stream1: 0' 이 출력됨.)
switchLatest의 정의
extension ObservableType where Element: ObservableConvertibleType {
/**
Transforms an observable sequence of observable sequences into an observable sequence
producing values only from the most recent observable sequence.
Each time a new inner observable sequence is received, unsubscribe from the
previous inner observable sequence.
- seealso: [switch operator on reactivex.io](http://reactivex.io/documentation/operators/switch.html)
- returns: The observable sequence that at any point in time produces the elements of the most recent inner observable sequence that has been received.
*/
public func switchLatest() -> Observable<Element.Element> {
Switch(source: self.asObservable())
}
}
switchLatest 를 호출하는 대상은 Observable<Element> 형태를 방출하는 Observable 이다.
여기서 'Element' 는 merge에서 보았듯이 'ObservableConvertibleType' 프로토콜을 준수하고 있는데,
'ObservableConvertibleType' 프로토콜은 해당 타입이 Observable로 변환될 수 있다는 것을 말한다고 했죠?
그래서 switchLatest를 사용할 수 있는 Observable<Element>은 방출하는 Element가 Observable로 사용될 수 있어야 한다는 것을 이렇게 표현하는 것 같다.
메서드 내부에서는 실제 동작을 수행하는 'Switch' 클래스의 인스턴스를 생성하여 반환한다.
5. startWith
주어진 Observable 스트림에 원하는 값을 시작점에 추가하고, 그 다음에 기존 스트림의 나머지 요소들이 방출된다.
따라서 첫 번째 이벤트가 발생되기 전에 특정 요소를 먼저 방출해야 할 때 사용된다.
(개인적인 생각으로 두 개 이상의 옵저버블이 결합된 것도 아니고, 지정한 값을 처음에 추가한 것 뿐인데 왜 결합 연산자로 분류될까?)
startWith 사용예시
var disposeBag: DisposeBag = .init()
let numbers = Observable.of(2, 3, 4)
numbers
.startWith(1)
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
//1
//2
//3
//4
설명이 필요없을 정도로 간단하죠?
numbers 옵저버블의 스트림 시작 부분에 '1' 을 추가한 것.
startWith의 정의
extension ObservableType {
/**
Prepends a sequence of values to an observable sequence.
- seealso: [startWith operator on reactivex.io](http://reactivex.io/documentation/operators/startwith.html)
- parameter elements: Elements to prepend to the specified sequence.
- returns: The source sequence prepended with the specified values.
*/
public func startWith(_ elements: Element ...)
-> Observable<Element> {
return StartWith(source: self.asObservable(), elements: elements)
}
}
매개변수 'elements' 는 가변 인자를 나타내며, 쉼표로 구분된 하나 이상의 값을 입력받는다.(1, 2, 3 ... 처럼 여러 값을 받을 수 있음) 그리고 입력된 값은 Observable이 방출하는 요소의 타입과 동일해야 한다.
startWith의 유용성
1. 초기 상태 설정
당연하게도 초기 값을 설정할 때 유용하다.
2. 데이터 누락 방지
특정 스트림에서 첫 번째 값이 늦게 도착하는 경우, UI에 빈 상태를 표시하는 것을 방지하기 위해 startWith을 통해 기본 값을 제공하면 좋다.
6. concat
두 개 이상의 Observable을 연결하여 하나의 Observable처럼 동작하게 만든다.
이 때, 각 Observable의 이벤트는 순서대로 연결된다.
merge에 대해서 알아볼 때, merge는 두 개 이상의 Observable을 하나로 결합하지만, 이들이 방출하는 이벤트의 순서는 보장하지 않았었다. 하지만 concat은 순서를 보장해준다는 의미!
순서 보장의 메커니즘은 첫 번째 Observable이 모든 항목을 방출한 후, 두 번째 Observable을 구독하는 형식이다.
따라서 첫 번째 Observable이 방출을 완료할 때까지 나머지 Observable은 기다려야만 한다.
concat 사용예시
import RxSwift
let firstObservable = Observable.of(1, 2, 3)
let secondObservable = Observable.of(4, 5, 6)
let concatenated = Observable.concat(firstObservable, secondObservable)
concatenated.subscribe(onNext: { value in
print(value)
})
//1
//2
//3
//4
//5
//6
concat을 이용하여 두 옵저버블을 결합하면, 첫 번째 옵저버블의 모든 요소(1, 2, 3)가 방출된 후 두 번째 옵저버블의 요소가 방출된다.
concat의 정의
concat은 'ObservableType' 의 extension 메서드로 정의되어 있으며,
concat은 여러 형태로 정의되어 있다.
1. 사용 예시에서 사용한 형태의 concat
public static func concat(_ sources: Observable<Element> ...) -> Observable<Element> {
Concat(sources: sources, count: Int64(sources.count))
}
매개변수 sources는 가변 인자로 결합하고 싶은 Observable들이 들어간다.
여러 개의 Observable을 전달할 수 있으며, 전달된 Observable이 순서대로 연결되는 concat이다.
2. Sequence를 사용하는 형태의 concat
public static func concat<Sequence: Swift.Sequence>(_ sequence: Sequence) -> Observable<Element>
where Sequence.Element == Observable<Element> {
return Concat(sources: sequence, count: nil)
}
이는 Sequence 프로토콜을 준수하는 객체를 받아서 안에 들어있는 Observable을 순차적으로 연결하는 concat이다.
매개변수 sequence는 'Sequence' 프로토콜을 준수해야 하며, 해당 시퀸스의 요소는 모두 Observable<Element> 타입이어야 한다.
Sequence를 사용하는 concat의 사용예시
let observable1 = Observable.of(1, 2, 3)
let observable2 = Observable.of(4, 5, 6)
let observable3 = Observable.of(7, 8, 9)
let sequenceOfObservables: [Observable<Int>] = [observable1, observable2, observable3]
let concatenated = Observable.concat(sequenceOfObservables)
concatenated.subscribe(onNext: { value in
print(value)
})
위 코드처럼 여러 Observable을 요소로 담고있는 배열에 concat을 적용하고 싶을 때 사용된다.
3. Collection을 사용하는 형태의 concat
public static func concat<Collection: Swift.Collection>(_ collection: Collection) -> Observable<Element>
where Collection.Element == Observable<Element> {
return Concat(sources: collection, count: Int64(collection.count))
}
'Sequence' 를 사용하는 형태와 유사하게, 'Collection' 프로토콜을 준수하는 객체를 받아서 안에 들어있는 Observable을 순차적으로 연결하는 concat이다.
Sequence와 Collection의 차이
-'Sequence' 프로토콜은 요소들의 정렬된 목록을 나타내며, 한 번 이상 순회할 수 있다.
하지만 인덱스, 길이 등의 개념이 없기 때문에 순회 후에 반드시 똑같이 순회한다는 보장이 없다.
-'Collection' 프로토콜은 'Sequence' 를 기반으로 만들어져 있으며,
요소들 사이에 정의된 순서가 있고, 여러 번 순회할 때, 동일한 순서로 순회할 수 있다는 보장이 있다.(인덱스와 길이의 개념이 존재함)
Collection를 사용하는 concat의 사용예시
let observable1 = Observable.of(1, 2, 3)
let observable2 = Observable.of(4, 5, 6)
let observable3 = Observable.of(7, 8, 9)
let collectionOfObservables: [Observable<Int>] = [observable1, observable2, observable3]
let concatenated = Observable.concat(collectionOfObservables)
concatenated.subscribe(onNext: { value in
print(value)
})
Sequence를 사용하는 concat의 형태와 동일하다.
concat의 유용성
1. 순서 보장
앞 차례에 있는 Observable의 이벤트가 모두 방출되고 나서야 다음 차례가 시작되기 때문에
이렇게 순차적으로 수행되어야 하는 작업에서 concat을 사용하면 유용하다.
위에서 zip 을 학습할 때, zip의 유용성에도 순서 보장을 넣었는데,
아무래도 순서보장의 면에서는 concat이 zip보다 적절해보인다.
concat의 주의점이나 한계점
1. 완료 작업
앞 차례의 Observable이 완료될 때까지 기다려야 하기 때문에 만약 첫 번째 Observable이 완료되지 못한다면,
나머지 Observable도 실행되지 않고, 이로 인해 다른 Observable로 처리해주어야 하는 작업이 있을 경우 문제가 발생할 수 있다. 따라서 각 Observable의 완료를 보장하는 로직이 필요할 수 있음!
Reference
-https://limjs-dev.tistory.com/132
-https://beepeach.tistory.com/202
'RxSwift' 카테고리의 다른 글
RxSwift에 관하여(Subject) (0) | 2023.08.09 |
---|---|
RxSwift에 관하여(Error Handling Operators) (0) | 2023.08.08 |
RxSwift에 관하여(Disposable) (0) | 2023.07.31 |
RxSwift에 관하여(Filtering Observables) (0) | 2023.07.27 |
RxSwift에 관하여(Transforming Observables) (0) | 2023.07.23 |