首页 > 解决方案 > 如何压缩超过 4 个发布者

问题描述

我正在使用 Swift Combine 来处理我的 API 请求。现在我面临一种情况,我想要将超过 4 个并行请求压缩在一起。在我使用 Zip4() 运算符将 4 个请求压缩在一起之前。我可以想象您分多个步骤进行压缩,但我不知道如何为其编写 receiveValue。

这是我当前代码的简化,包含 4 个并行请求:

    Publishers.Zip4(request1, request2, request3, request4)
        .sink(receiveCompletion: { completion in
            // completion code if all 4 requests completed
        }, receiveValue: { request1Response, request2Response, request3Response, request4Response in
            // do something with request1Response
            // do something with request2Response
            // do something with request3Response
            // do something with request4Response
        }
    )
        .store(in: &state.subscriptions)

标签: swiftcombine

解决方案


阻止您压缩任意数量的发布者的是一个非常不幸的事实,即 Apple 选择将 zip 运算符的输出设为tuple。元组非常不灵活,并且它们的能力有限。你不能有一个元组,比如说,十个元素;而且您甚至不能将元素附加到元组,因为这会导致您获得不同的类型。因此,我们需要的是一个新的操作符,它可以做同样的工作,zip但会产生一些更强大、更灵活的结果,比如数组。

我们可以制作一个!幸运的是,zip操作符本身有一个transform参数,可以让我们指定我们想要的输出类型。

好的,为了说明,我将十个出版商压缩在一起。首先,我将创建一个包含 10 个发布者的数组;他们将仅仅是 Just 出版商,但这足以说明这一点,并且为了证明我没有作弊,我将任意延迟他们每个人:

let justs = (1...10).map {
    Just($0)
        .delay(for: .seconds(Int.random(in:1...3)), scheduler: DispatchQueue.main)
        .eraseToAnyPublisher() }

好的,现在我有一系列发布者,我将它们循环压缩在一起:

let result = justs.dropFirst().reduce(into: AnyPublisher(justs[0].map{[$0]})) { 
    res, just in
    res = res.zip(just) {
        i1, i2 -> [Int] in
        return i1 + [i2]
    }.eraseToAnyPublisher()
}

注意运算符后面的尾随闭包zip!这确保我的输出将是一个Array<Int>而不是一个元组。与元组不同,我可以创建任意大小的数组,只需每次通过循环添加元素。

好的,result现在是一个 Zip 发布者,它将十个发布者压缩在一起。为了证明这一点,我只需将订阅者附加到它并打印输出:

result.sink {print($0)}.store(in: &self.storage)

我们运行代码。有一个令人心碎的停顿——没错,因为每个 Just 发布者都有不同的随机延迟,而 zip 的规则是他们需要在我们得到任何输出之前发布。他们迟早都会这样做,并且输出会出现在控制台中:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

完全正确的答案!我已经证明我确实将十个出版商压缩在一起,以产生由他们每个人的单一贡献组成的输出。

将任意数量的数据任务发布者(或您正在使用的任何东西)压缩在一起也没有什么不同。

(有关我学习如何序列化任意数量的数据任务发布者的相关问题,请参阅组合框架序列化异步操作。)


推荐阅读