swift - Swift Combine:未来会做异步工作吗?
问题描述
我有一个名为的对象ProcessorStack
,其中包含零个或多个子Processor
项。ProcessorStack
和单独的对象Processor
每个只有一个方法:
process(input: Value) -> Future<Value, Never>
我希望返回值是 aFuture
而不是AnyPublisher
清楚地表明调用者应该只期望发出一个结果。其他对象只能访问ProcessorStack
,而不是其Processor
子对象。这就是我想要发生的事情:
- 一个对象调用
ProcessorStack
:stack.process(value: someValue).sink { result in // Do something with the result }.store(in: &subscriptions)
- 使用 reduce 操作将其
ProcessorStack
所有子Processor
对象链接在一起,并通过以下方式返回最终结果Future
:func process(value: Value) -> Future<Value, Never> { guard !childProcessors.isEmpty else { return Future { $0(.success(value)) } } let just = Just(value).eraseToAnyPublisher() childProcessors.reduce(just) { (publisher, processor) -> AnyPublisher<Value, Never> in publisher.flatMap { processor.process(value: $0).eraseToAnyPublisher() } } // Here's where I'm lost. }
我一生都无法弄清楚如何执行异步 reduce 链,然后将结果作为Future
. 如果我将整个 reduce 操作包装在一个Future
初始化程序中,我将保留一个AnyPublisher<Value, Never>
我必须以某种方式执行的操作,然后将其结果传递给Future
's 的完成闭包。我不能sink
在Future
's 闭包中使用它,因为我必须坚持从那里返回的可取消,否则整个过程会立即停止。我不能将结果平面映射到 Future,因为它的类型是FlatMap<AnyPublisher<Value, Never>, Future<Value, Never>>
. 如果我只制作外部返回类型AnyPublisher<Value, Never>
,我可以完成所有这些,但我真的希望拥有Future
订阅者的语义。
解决方案
您尝试做的事情的前提是错误的。Future
,尽管它最多只能返回一个结果,但在语义上并不代表这一点。它只是一种特定类型的发布者。
您应该AnyPublisher
在函数边界返回 a ,而不是试图避免它,这将使您的代码对更改更加健壮(例如,如果您需要将 a 包装Future
在 a 中Deferred
怎么办? - 一种常见做法)
process(value: Value) -> AnyPublisher<Value, Never> {
...
}
如果订阅者只能处理一个结果,他们可以简单地确保first()
:
process(value)
.first()
.sink {...}
.store(in: &storage)
但是如果你坚持,你可以使用.sink
insideFuture
的闭包,如果闭包捕获了对的引用AnyCancellable
并在完成时释放它:
process(value: Value) -> Future<Value, Never> {
guard !childProcessors.isEmpty else {
return Future { $0(.success(value)) }
}
let just = Just(value).eraseToAnyPublisher()
let combined = childProcessors.reduce(just) { (publisher, processor) -> AnyPublisher<Value, Never> in
publisher.flatMap { processor.process(value: $0).eraseToAnyPublisher() }
}
var c: AnyCancellable? = nil
return Future { promise in
c = combined.sink(receiveCompletion: {
withExtendedLifetime(c){}; c = nil
}) {
promise(.success($0))
}
}
}
推荐阅读
- asp.net - 将excel文件保存在特定文件夹中
- mysql - 无法从外部访问 vagrant MySQL 数据库(仅当 vagrant ssh 进入带有 Scotchbox 的盒子时)
- android - 从另一个 xml 文件定义
- jquery - 如何单独影响每个项目的 foreach 项目样式(悬停)
- python - 在不使用循环和经典 python 代码的情况下合并间隔
- sql - 创建每日 Oracle 分区
- r - 如何在 R 中以闪亮的方式强制数据框中的反应
- python - TypeError:列表索引必须是整数或切片,而不是字符串
- c++ - 在这种情况下,为什么不是 constexpr ?
- neural-network - 当用户提出问题时,从 CSV 文件中的数据预测答案