在程式執行時,我們如果打 API 往往會用 background thread,以免在 main Thread 影響到 App 的 效能。RxSwift 是一個很強大的套件,透過 RxSwift 我們可以很快速切換線程,讓我們查詢的工作在背景執行,並且在 main Thread 更新 UI,而這完全是依靠 subscribeOn 和 observeOn,怎麼做到的呢?讓我們來看一下。
![[RxSwift] subscribeOn 和 observeOn 的差異 1 swift 1](https://www.inote.tw/wp-content/uploads/2020/02/swift-1.png)
假設,我有一個 API,要依價錢來算損益,為了讓他更複雜點,所以,我可能寫成這樣。
/// 計算損益結果
func genRevenueResultObservable(price: String) -> Observable<Int> {
Observable.create { [unowned self] observer in
/// 下面的 print 是使用 subscribeOn 會跑的 Thread 線程
print("Thread: \(Thread.current.isMainThread ? "MainThread": "Not Main Thread"), Line: \(#line)")
observer.onNext(Int(getRevenueResult(price: price)) ?? 0)
observer.onCompleted()
return Disposables.create()
}
}
/// 取得 API
func getRevenueAPI(price: String) -> Observable<String> {
Observable.create { [unowned self] observer in
/// 下面的 print 是使用 subscribeOn 會跑的 Thread 線程
print("Thread: \(Thread.current.isMainThread ? "MainThread": "Not Main Thread"), Line: \(#line)")
genRevenueResultObservable(price: price)
.subscribe(onNext: { result in
/// 下面的 print 是使用 observeOn 會跑的 Thread 線程
print("Thread: \(Thread.current.isMainThread ? "MainThread": "Not Main Thread"), Line: \(#line)")
observer.onNext("\(result)")
observer.onCompleted()
}).disposed(by: disposeBag)
return Disposables.create()
}
}
/// 取得計算結果
func getRevenueResult(price: String) -> String {
return price
}
所以,我們可以用 subscribeOn 來讓生成的 Observable 要在那個線程執行,而在生成的 Observable 被訂閱後,我們可以用 observeOn 決定要在那個線程執行,簡單的範例如下。
for i in 0...100 {
let observe = getRevenueAPI(price: "\(i)")
observe.subscribe(on: ConcurrentDispatchQueueScheduler(qos: .background))
.observe(on: MainScheduler.instance)
.subscribe{ [unowned self] _ in
/// 在主線程執行
print("Revenue: \(revenue)")
print("Thread: \(Thread.current.isMainThread ? "MainThread": "Not Main Thread"), Line: \(#line)")
}.disposed(by: disposeBag)
}
以上述範例來說,我們取 getRevenueAPI 這裡面的,都會在 background thread 執行,而當有結果回來,會跑「 print(“Revenue: (revenue)”)」這段程式,因為我們有設定「observe(on: MainScheduler.instance)」,所以就會在 main Thread 印出這段程式。
而底下是另一個範例,因為我們有設定「observe(on: ConcurrentDispatchQueueScheduler(qos: .background))」,所以這段程式「 print(“Revenue: (revenue)”)」就會在背景執行。
for i in 0...100 {
let observe = getRevenueAPI(price: "\(i)")
observe.subscribe(on: ConcurrentDispatchQueueScheduler(qos: .background))
.observe(on: ConcurrentDispatchQueueScheduler(qos: .background))
.subscribe{ [unowned self] _ in
/// 在背景線程執行
print("Revenue: \(revenue)")
print("Thread: \(Thread.current.isMainThread ? "MainThread": "Not Main Thread"), Line: \(#line)")
}.disposed(by: disposeBag)
}
底下是最後一個範例,如果我們沒有寫「.subscribe(on: ConcurrentDispatchQueueScheduler(qos: .background))」,則視同在 main Thread 訂閱生成的 Observable,那麼就會在 main Thread 去執行 getRevenueAPI 這隻 API,然後在背景執行印出收益,所以如果有 API 可能比較慢,可能就會卡住畫面了。
for i in 0...100 {
let observe = getRevenueAPI(price: "\(i)")
observe.observe(on: ConcurrentDispatchQueueScheduler(qos: .background))
.subscribe{ [unowned self] _ in
/// 在背景線程執行
print("Revenue: \(revenue)")
print("Thread: \(Thread.current.isMainThread ? "MainThread": "Not Main Thread"), Line: \(#line)")
}.disposed(by: disposeBag)
}
簡單來說,如果是在 Observable.create 內的程式 subscribeOn 所決定的線程,如果是 subscribe 內的程式碼,都是跑 observeOn 所決定的線程,如果都不寫,預設就都在主線程上執行。而以上是基本的使用方式,希望有幫助到您。