[RxSwift] subscribeOn 和 observeOn 的差異

在程式執行時,我們如果打 API 往往會用 background thread,以免在 main Thread 影響到 App 的 效能。RxSwift 是一個很強大的套件,透過 RxSwift 我們可以很快速切換線程,讓我們查詢的工作在背景執行,並且在 main Thread 更新 UI,而這完全是依靠 subscribeOn 和 observeOn,怎麼做到的呢?讓我們來看一下。

swift 1

假設,我有一個 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 所決定的線程,如果都不寫,預設就都在主線程上執行。而以上是基本的使用方式,希望有幫助到您。

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments