【问题标题】:How do you run a Swift Combine Publisher for a certain amount of time?如何在一定时间内运行 Swift Combine Publisher?
【发布时间】:2021-10-29 00:16:05
【问题描述】:

我有一个发布者,当接收器扫描 wifi 列表时。我只想扫描大约 10 秒然后停下来。

有没有办法在发布者调用链中做到这一点?

【问题讨论】:

  • 您希望多久扫描一次?您想在时间限制(例如 10 秒)后停止扫描,还是在一定次数的扫描后(例如 10 次扫描后,每次扫描间隔 1 秒)停止扫描?
  • 是否有一些触发器应该开始重新扫描?
  • 扫描被调用一次,数据每秒返回一次。我想在 10 秒后停下来。 @亚历山大
  • 您是否使用 combine 来触发每次扫描?
  • @亚历山大。不,即 wifiListPublisher.sink... wifiListPublisher 是一个自定义发布者,每秒使用 wifi ssid 进行调用和输出。

标签: swift combine publisher


【解决方案1】:

这个操作符可以解决问题。

import PlaygroundSupport
import Foundation
import Combine

let page = PlaygroundPage.current
page.needsIndefiniteExecution = true

extension Publisher {
    func stopAfter<S>(_ interval: S.SchedulerTimeType.Stride, tolerance: S.SchedulerTimeType.Stride? = nil, scheduler: S, options: S.SchedulerOptions? = nil) -> AnyPublisher<Output, Failure> where S: Scheduler {
        prefix(untilOutputFrom: Just(()).delay(for: interval, tolerance: tolerance, scheduler: scheduler, options: nil))
            .eraseToAnyPublisher()
    }
}

let source = Timer.publish(every: 1, tolerance: nil, on: RunLoop.main, in: .default, options: nil)
    .autoconnect()
    .eraseToAnyPublisher()

let cancellable = source
    .stopAfter(10, scheduler: DispatchQueue.main)
    .sink(receiveValue: { print($0) })

【讨论】:

  • 超时发布者可以简化为Just(1).delay(for: 10, scheduler: DispatchQueue.main)
  • @Cristik 感谢您的改进。我还像你一样将函数包装在一个新的运算符中。
【解决方案2】:

您可以使用timeout() 运算符:

如果上游发布者超过指定的时间间隔而不产生元素,则终止发布。

wifiScannerPublisher
    .timeout(.seconds(waitTime), scheduler: DispatchQueue.main, options: nil, customError:nil)
    .sink(
        receiveCompletion: { print("completion: \($0), at: \(Date())") },
        receiveValue: { print("wifi: \($0)") }
     )

但是,如果您的发布者一直定期发送事件,而您只是想在一段时间后停止它,那么Daniel's 答案可能是要走的路。

不过,我会自己添加一个解决方案,通过使用timeout()scan()Publisher 扩展:

extension Publisher {
    func stopAfter(_ interval: TimeInterval) -> AnyPublisher<Output, Failure> {
        self
            .timeout(.seconds(interval), scheduler: DispatchQueue.main)
            .scan((Date()+interval, nil)) { ($0.0, $1) }
            .prefix(while: { Date() < $0.0 })
            .map { $0.1! }
            .eraseToAnyPublisher()
    }
}

上述发布者将携带超时日期,一旦达到该日期,它将停止。需要map来丢弃物品携带的额外Date

用法:

wifiListPublisher.stopAfter(10)
    .sink(...)

【讨论】:

  • 这不适用于我的场景,因为我需要在一段时间内收集输出流而不会出错。
  • 在错误出现时应该已经发送了输出值。但是,如果您不希望出现错误,您可以catch 发布者。取决于您的 wifi 发布者如何发出值。 @Unikorn
  • 如果您像 wifi 扫描一样继续接收值,超时不会停止。来自苹果文档:如果上游发布者超过指定的时间间隔而不产生元素,则终止发布。
  • @Unikorn,所以您希望发布者在 10 秒后结束,无论是否不断发现新的 wifi?
  • @Unikorn 是的,在这种情况下,丹尼尔的解决方案是可行的方法。
猜你喜欢
  • 2020-03-07
  • 2020-10-13
  • 2019-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-12
  • 1970-01-01
相关资源
最近更新 更多