1. Khái niệm:
Subject trong RxSwift hoạt động như vừa là một
Observable, vừa là một
Observer. Khi một
Subject nhận một
.next event thì ngay lập tức nó sẽ phát ra các emit cho các
subscriber của nó.
2. Khái quát:
Trong RxSwift, chúng ta có 4 loại Subject với các cách thức hoạt động khác nhau, bao gồm:
- PublishSubject: Khởi đầu "empty" và chỉ emit các element mới cho subscriber của nó.
- BehaviorSubject: Khởi đầu với một giá trí khởi tạo và sẽ relay lại element cuối cùng của chuỗi cho Subscriber mới.
- ReplaySubject: Khởi tạo với một kích thước bộ đệm
cố định, sau đó sẽ lưu trữ các element gần nhất vào bộ đệm này và relay
lại các element chứa trong bộ đệm cho một Subscriber mới.
- Variable: Lưu trữ một giá trị như một state và sẽ relay duy nhất giá trị cuối cùng cho Subscriber mới.
3. Chi tiết:
3.1. PublishSubject:
Publish subjects được sử dụng khi bạn chỉ muốn subscribers được thông
báo về các sự kiện mới từ thời điểm bạn subscribe cho đến khi hủy
subscribe hoặc Subject đã chấm dứt với sự kiện khi .completed hoặc
.error

Để hiểu sâu hơn về cách thức hoạt động của PublishSubject chúng ta hãy cùng theo dõi một ví dụ
let bag = DisposeBag()
let publishSubject = PublishSubject<String>()
publishSubject.onNext("Emit 1")
let subscriberOne = publishSubject.subscribe { element in
print("subscriber 1: \(element)")
}
subscriberOne.disposed(by: bag)
publishSubject.onNext("Emit 2")
publishSubject.onNext("Emit 3")
Chúng ta thu được kết quả như sau
subscriber 1: next(Emit 2)
subscriber 1: next(Emit 3)
*
Lưu ý: Tất cả các subject khi đã terminate thì vẫn
re-emit stop event cho các subscriber mới trong tương lai. Chúng ta có
thể thấy rõ điều đó thông qua ví dụ sau
let bag = DisposeBag()
let publishSubject = PublishSubject<String>()
publishSubject.onNext("Emit 1")
let subscriberOne = publishSubject.subscribe { element in
print("subscriber 1: \(element)")
}
subscriberOne.disposed(by: bag)
publishSubject.onNext("Emit 2")
publishSubject.onNext("Emit 3")
publishSubject.onCompleted()
print("- - - - - - -")
let subscriberTwo = publishSubject.subscribe { element in
print("subscriber 2: \(element)")
}
subscriberTwo.disposed(by: bag)
Kết quả
subscriber 1: next(Emit 2)
subscriber 1: next(Emit 3)
subscriber 1: completed
- - - - - - -
subscriber 2: completed
3.2 BehaviorSubject:
BehaviorSubject hoạt động tương tự như PublishSubject, nhưng chỉ khác
ở chỗ BehaviorSubject khởi đầu với một giá trị và replay lại giá trị đó
hoặc .next event cuối cùng của observable cho một Subscriber mới.

Chúng ta cũng theo dõi ví dụ sau
let bag = DisposeBag()
let behaviorSubject = BehaviorSubject<String>(value: "Initial Value")
behaviorSubject.onNext("Emit 1")
print("- Subscribe here -")
let subscriber = behaviorSubject.subscribe { element in
print("Subscriber: \(element)")
}
subscriber.disposed(by: bag)
behaviorSubject.onNext("Emit 2")
behaviorSubject.onNext("Emit 3")
Chúng ta thu được kết quả
- Subscribe here -
Subscriber: next(Emit 1)
Subscriber: next(Emit 2)
Subscriber: next(Emit 3)
Subscriber vẫn nhận được "Emit 1" bởi tại thời điểm nó subscribe thì
"Emit 1" chính là element cuối cùng trong Observable của subject nên
subject đã replay lại element này cho subscriber.
3.3 ReplaySubject:
Được khởi tạo với một kích thước bộ đệm và sẽ chứa số lượng các emit
gần nhất bằng với kích thước bộ đệm đã khai báo. ReplaySubject sẽ replay
lại tất cả các emit trong bộ đệm cho subscriber ngay khi subscriber
đăng ký.

Để hiểu rõ hơn về cơ hế hoạt động của ReplaySubject, chúng ta cùng theo dõi ví dụ dưới
let bag = DisposeBag()
let replaySubject = ReplaySubject<String>.create(bufferSize: 2)
replaySubject.onNext("Emit 1")
replaySubject.onNext("Emit 2")
replaySubject.onNext("Emit 3")
print("- Before subscribe -")
let subscriber = replaySubject.subscribe { element in
print("Subscriber: \(element)")
}
subscriber.disposed(by: bag)
print("- After subscribe -")
replaySubject.onNext("Emit 4")
replaySubject.onNext("Emit 5")
Chúng ta được kết quả
- Before subscribe -
Subscriber: next(Emit 2)
Subscriber: next(Emit 3)
- After subscribe -
Subscriber: next(Emit 4)
Subscriber: next(Emit 5)
Giờ chúng ta thử tăng buffer size lên 3 trong đoạn code trên xem thu được kết quả ra sao nhé
let replaySubject = ReplaySubject<String>.create(bufferSize: 3)
Kết quả
- Before subscribe -
Subscriber: next(Emit 1)
Subscriber: next(Emit 2)
Subscriber: next(Emit 3)
- After subscribe -
Subscriber: next(Emit 4)
Subscriber: next(Emit 5)
3.4 Variable:
Lưu trữ dữ liệu hiện tại của BehaviorSubject như một state và replay
duy nhất giá trị khởi tạo hoặc giá trị cuối cùng của dữ liệu cho
subscriber mới.
let bag = DisposeBag()
let variableSubject = Variable("Initial Value")
let subscriber = variableSubject.asObservable()
.subscribe { value in
print("Subscriber: \(value)")
}
subscriber.disposed(by: bag)
variableSubject.value = "New value"
Kết quả
Subscriber: next(Initial Value)
Subscriber: next(New value)
Tuy nhiên, Variable trong tương lai sẽ không còn được sử dụng thay vào đó chúng ta sẽ sử dụng
BehaviorRelay.
BehaviorRelay nằm trong RxCocoa, nhưng nó tương tự như
Variable, chỉ khác về mặt cú pháp khai báo và sử dụng nó.
let bag = DisposeBag()
let behaviorRelay = BehaviorRelay<Bool>(value: false)
let subscriber = behaviorRelay.asObservable()
.subscribe { element in
print("Subscriber: \(element)")
}
subscriber.disposed(by: bag)
behaviorRelay.accept(false)
behaviorRelay.accept(true)
Kết quả
Subscriber: next(false)
Subscriber: next(false)
Subscriber: next(true)
Comments
Post a Comment