一尘不染

结合框架序列化异步操作

swift

如何使构成Combine框架的异步管道同步(串行)排列?

假设我有50个URL,我想从中下载相应的资源,假设我想一次完成一个。我知道如何使用Operation /
OperationQueue做到这一点,例如,使用在下载完成之前不会声明自身完成的Operation子类。我如何使用Combine做同一件事?

目前我所要做的就是保留剩余URL的全局列表并弹出一个列表,为一个下载设置一个管道,进行下载,然后在sink该管道中重复执行。看起来不太像Combine。

我确实尝试制作了一系列URL,并将其映射到一系列发布者。我知道我可以“产生”一个发布者,并使其使用进行发布flatMap。但是,我仍然同时进行所有下载。没有任何可组合方式以受控方式遍历数组-
或存在吗?

(我也曾想过要对Future进行一些操作,但我变得无望地感到困惑。我不习惯这种思维方式。)


阅读 189

收藏
2020-07-07

共1个答案

一尘不染

我仅对此进行了简短的测试,但是乍一看似乎每个请求在开始之前都等待上一个请求完成。

我正在发布此解决方案以寻求反馈。如果这不是一个好的解决方案,请务必提出批评。

extension Collection where Element: Publisher {

    func serialize() -> AnyPublisher<Element.Output, Element.Failure>? {
        // If the collection is empty, we can't just create an arbititary publisher
        // so we return nil to indicate that we had nothing to serialize.
        if isEmpty { return nil }

        // We know at this point that it's safe to grab the first publisher.
        let first = self.first!

        // If there was only a single publisher then we can just return it.
        if count == 1 { return first.eraseToAnyPublisher() }

        // We're going to build up the output starting with the first publisher.
        var output = first.eraseToAnyPublisher()

        // We iterate over the rest of the publishers (skipping over the first.)
        for publisher in self.dropFirst() {
            // We build up the output by appending the next publisher.
            output = output.append(publisher).eraseToAnyPublisher()
        }

        return output
    }
}

此解决方案的更简洁版本(由@matt提供):

extension Collection where Element: Publisher {
    func serialize() -> AnyPublisher<Element.Output, Element.Failure>? {
        guard let start = self.first else { return nil }
        return self.dropFirst().reduce(start.eraseToAnyPublisher()) {
            $0.append($1).eraseToAnyPublisher()
        }
    }
}
2020-07-07