合并:延迟发布序列的元素

Combine: publish elements of a sequence with some delay

我是Combine新手,想弄个看似简单的东西。假设我有一个整数集合,例如:

let myCollection = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

我想延迟发布每个元素,例如 0.5 秒。

print 0
wait for 0.5secs
print 1
wait for 0.5secs
and so forth

我可以轻松获取序列发布者并像这样打印元素:

let publisherCanc = myCollection.publisher.sink { value in
    print(value)
}

但在这种情况下,所有值都会立即打印出来。如何延迟打印值?在 Combine 中有一个 .delay 修饰符,但它不是我需要的(实际上,.delay 会延迟整个流而不是单个元素)。如果我尝试:

let publisherCanc = myCollection.publisher.delay(for: .seconds(0.5), scheduler: RunLoop.main).sink { value in
    print(value)
}

我得到的只是 "initial" 延迟,然后立即打印元素。

感谢您的帮助。

使用评论中 linked by Alexander 的想法,您可以使用 Timer.publish(every:on:in:) 创建一个每 0.5 秒发出一个值的发布者,然后 zip 与您的 Array.publisher 使您的下游发布者在每次您的两个发布者都发出一个新值时发出一个值。

Publishers.Zip 获取其上游发布者的第 n 个元素,并且仅在其两个上游都达到 n 发射值时才发射 - 因此通过将仅在 [= 发射其值的发布者压缩在一起15=] 与立即发出其所有值的原始发布者的间隔,您将每个值延迟 0.5 秒。

let delayPublisher = Timer.publish(every: 0.5, on: .main, in: .default).autoconnect()
let delayedValuesPublisher = Publishers.Zip(myCollection.publisher, delayPublisher)
let subscription = delayedValuesPublisher.sink { print([=10=].0) }

尝试将 flatMap(maxPublishers:) 与 delay(for:scheduler:) 运算符一起使用。

import Foundation
import Combine

var tokens: Set<AnyCancellable> = []

let valuesToPublish = [1, 2, 3, 4, 5, 6, 7, 8, 9]
valuesToPublish.publisher
    .flatMap(maxPublishers: .max(1)) { Just([=10=]).delay(for: 1, scheduler: RunLoop.main) }
    .sink { completion in
        print("--- completion \(completion) ---")
    } receiveValue: { value in
        print("--- value \(value) ---")
    }
    .store(in: &tokens)

设置 maxPublishers 属性 您可以指定 concurrent 发布者订阅的最大数量。 Apple

根据其他答案中提供的示例,我想出了一个使用泛型的解决方案:

import Combine
import SwiftUI

struct TimedSequence<T: Any>  {
    typealias TimedJointPublisher = (Publishers.Zip<Publishers.Sequence<[T], Never>, Publishers.Autoconnect<Timer.TimerPublisher>>)
    
    var sink: AnyCancellable?

    init(array: [T], interval: TimeInterval, closure: @escaping (T) -> Void) {
        let delayPublisher = Timer.publish(every: interval, on: .main, in: .default).autoconnect()
        let timedJointPublisher = Publishers.Zip(array.publisher, delayPublisher)
        self.sink = timedJointPublisher.sink(receiveValue: {r in
            closure(r.0)
        })
    }
}

用法:

1 - 基本类型:

let m = TimedSequence(array: [1, 2, 3], interval: 2, closure: {
    element in
    let textReceived = String(element)        //assigns 1 ...2 seconds...then 2...2 seconds...then 3 to textReceived
})

let m = TimedSequence(array: ["Hello", "World"], interval: 2, closure: {
    element in
    let textReceived = element.upperCased()   //assigns HELLO ...2 seconds... then WORLD to textReceived

2 - 自定义类型:

class MyClass {
    var desc: String
    init(desc: String) {
        self.desc = desc
    }
}


let m = TimedSequence(array: [MyClass(desc: "t"), MyClass(desc: "s")], interval: 2, closure: {
    str in
    let textReceived = str.desc.uppercased()
})

与 1 相同,除了 str.desc(分别为 S 和 T)以 2 秒的间隔分配给 textReceived