简体   繁体   中英

delaySubscription doesn't work with rx_tap

This is a short version of my code which will reproduce the problem:

import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {
    @IBOutlet weak var button: UIButton!

    let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        let source = button.rx_tap.map { _ in "source" }

        let delay = source.map { _ in "delayed" }
            .delaySubscription(2.0, MainScheduler.sharedInstance)

        [source, delay].toObservable().merge()
            .subscribeNext { print($0) }
            .addDisposableTo(disposeBag)
    }
}

I want the 'delayed' signal to fire 2 seconds after I tap the button, but no such luck. What actually happens: the first time I tap the button, 'source' fires but nothing else happens. Then when I tap again, 'source' and 'delayed' fire at the same time. I figured it was some thread problem, so I tried adding observeOn(MainScheduler.sharedInstance) everywhere but it didn't help. Any ideas?

Update: by adding .debug() to the streams I found out that the delayed stream actually subscribes to the source 2 seconds later. But that still doesn't explain why it doesn't fire its notifications 2 seconds later as well.

To answer my own question, it seems that delaySubscription only works on cold observables.

A cold observable, like for example a timer , only starts to fire notifications when it has been subscribed to, and everyone that subscribes to it gets a fresh sequence. This is why simply delaying the subscription on a cold observable will also delay all the notifications.

A hot observable, like a UI event for example, shares the same sequence with all its subscribers, so delaying the subscription has absolutely no influence on its notifications.

Instead, I can use the flatMap operator to transform each source notification into another observable that fires its only notification after a certain delay, and merges the results of these observables:

class ViewController: UIViewController {
@IBOutlet weak var button: UIButton!

let disposeBag = DisposeBag()

override func viewDidLoad() {
    super.viewDidLoad()

    let source = button.rx_tap.map { _ in "source" }

    let delayed = source.flatMap { _ in
        timer(1.0, MainScheduler.sharedInstance)
            .map { _ in "delayed" }
    }

    [source, delayed]
        .toObservable().merge()
        .subscribeNext { print($0) }
        .addDisposableTo(disposeBag)
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM