Ios 组合框架:如何在继续之前异步处理数组的每个元素
我在使用iOS Combine框架时遇到了一点心理障碍 我正在将一些代码从“手动”从远程API获取转换为使用Combine。基本上,API是SQL和REST(实际上是Salesforce,但这与问题无关)。代码用来调用一个REST查询方法,该方法接受一个完成处理程序。我正在做的是用联合收割机的未来来取代这一切。到目前为止,一切顺利 当以下情况发生时(并且经常发生),问题就会出现:Ios 组合框架:如何在继续之前异步处理数组的每个元素,ios,swift,combine,Ios,Swift,Combine,我在使用iOS Combine框架时遇到了一点心理障碍 我正在将一些代码从“手动”从远程API获取转换为使用Combine。基本上,API是SQL和REST(实际上是Salesforce,但这与问题无关)。代码用来调用一个REST查询方法,该方法接受一个完成处理程序。我正在做的是用联合收割机的未来来取代这一切。到目前为止,一切顺利 当以下情况发生时(并且经常发生),问题就会出现: 我们执行REST查询并返回一个“对象”数组 但这些“对象”并没有完全填充。它们中的每一个都需要来自某个相关对象的附加
导致
,这就是异步网络调用的完成处理程序
好的,我很清楚如何在Combine中链接异步调用,例如使用Futures和flatMap
(再次使用伪代码):
在该代码中,我们形成future2
的方式取决于我们从执行future1
中收到的值,并且在future2
上的map
中,我们可以修改我们从上游收到的内容,然后再将其传递到管道中。没问题。一切都很美
但这并没有告诉我我在预组合代码中做了什么,即循环。在这里,我在一个循环中执行多个异步调用,在继续之前由DispatchGroup保留。问题是:
这样做的联合收割机模式是什么
记住当时的情况。我有一些物体的数组。我想循环该数组,对循环中的每个对象执行异步调用,异步获取新信息,并在此基础上修改该对象,然后再继续执行管道。每个循环可能涉及一个进一步的嵌套循环,该循环异步收集更多信息:
Fetch info from online database, it's an array
|
V
For each element in the array, fetch _more_ info, _that's_ an array
|
V
For each element in _that_ array, fetch _more_ info
|
V
Loop thru the accumulated info and populate that element of the original array
执行此操作的旧代码看起来很糟糕,充满了嵌套的完成处理程序和由DispatchGroupenter
/leave
/notify
保留的循环。但它奏效了。我不能让我的合并代码以同样的方式工作。我该怎么做?基本上,我的管道输出是一个数组,我觉得我需要将该数组拆分为单个元素,对每个元素异步执行一些操作,然后将这些元素重新组合到一个数组中。怎么做
我解决这个问题的方法是可行的,但不能扩展,特别是当异步调用需要在管道链中返回几步的信息时。我一直在做这样的事情(我的想法来自):
flatMap
和map
将数组映射到一个发布者数组,每个发布者都有一个未来,它获取与一个对象相关的更多在线内容,然后是一个生成修改对象的管道merge
该数组并从flatMap
生成该发布者(MergeMany)将结果值收集回数组中
必须有一些简单的组合模式来做这件事,但我完全错过了。请告诉我它是什么。使用公认的答案,我总结了以下结构:
head // [Entity]
.flatMap { entities -> AnyPublisher<Entity, Error> in
Publishers.Sequence(sequence: entities).eraseToAnyPublisher()
}.flatMap { entity -> AnyPublisher<Entity, Error> in
self.makeFuture(for: entity) // [Derivative]
.flatMap { derivatives -> AnyPublisher<Derivative, Error> in
Publishers.Sequence(sequence: derivatives).eraseToAnyPublisher()
}
.flatMap { derivative -> AnyPublisher<Derivative2, Error> in
self.makeFuture(for: derivative).eraseToAnyPublisher() // Derivative2
}.collect().map { derivative2s -> Entity in
self.configuredEntity(entity, from: derivative2s)
}.eraseToAnyPublisher()
}.collect()
head//[实体]
.flatMap{entities->AnyPublisher中的
publisher.Sequence(Sequence:entities).橡皮擦到任何publisher()
}.flatMap{entity->AnyPublisher中的
self.makeFuture(针对:实体)/[衍生工具]
.flatMap{衍生工具->中的任意发布者
publisher.Sequence(Sequence:developers).橡皮擦到任何publisher()
}
.flatMap{派生->中的任意发布者
self.makeFuture(for:derivative).eraseToAnyPublisher()//Derivative2
}.collect().map{derivative2s->中的实体
自配置身份(实体,来源:派生)
}.删除任何发布者()
}.collect()
那正是我想要的那种优雅的松紧感!因此,我们的想法是:
我们接收一个数组,需要异步处理每个元素。旧的方法应该是一个DispatchGroup和一个for…in
循环。联合收割机当量为:
- 与行中…的
等效的是
和publisher.SequenceflatMap
- DispatchGroup(处理异步)的等价物是另一个
(在单个元素上)和一些发布者。在我的例子中,我从一个基于我们刚刚收到的单个元素的未来开始flatMap
- 末端右大括号的等价物是
,它等待处理所有元素并将数组重新组合在一起collect()
flatMap
t
Fetch info from online database, it's an array
|
V
For each element in the array, fetch _more_ info, _that's_ an array
|
V
For each element in _that_ array, fetch _more_ info
|
V
Loop thru the accumulated info and populate that element of the original array
head // [Entity]
.flatMap { entities -> AnyPublisher<Entity, Error> in
Publishers.Sequence(sequence: entities).eraseToAnyPublisher()
}.flatMap { entity -> AnyPublisher<Entity, Error> in
self.makeFuture(for: entity) // [Derivative]
.flatMap { derivatives -> AnyPublisher<Derivative, Error> in
Publishers.Sequence(sequence: derivatives).eraseToAnyPublisher()
}
.flatMap { derivative -> AnyPublisher<Derivative2, Error> in
self.makeFuture(for: derivative).eraseToAnyPublisher() // Derivative2
}.collect().map { derivative2s -> Entity in
self.configuredEntity(entity, from: derivative2s)
}.eraseToAnyPublisher()
}.collect()
func getFoos() -> AnyPublisher<[Foo], Error>
func getPartials(for: Foo) -> AnyPublisher<[Partial], Error>
func getMoreInfo(for: Partial, of: Foo) -> AnyPublisher<MoreInfo, Error>
getFoos()
.flatMap { fooArr in
fooArr.publisher.setFailureType(to: Error.self)
}
// per-foo element async processing
.flatMap { foo in
getPartials(for: foo)
.flatMap { partialArr in
partialArr.publisher.setFailureType(to: Error.self)
}
// per-partial of foo async processing
.flatMap { partial in
getMoreInfo(for: partial, of: foo)
// build completed partial with more info
.map { moreInfo in
var newPartial = partial
newPartial.moreInfo = moreInfo
return newPartial
}
}
.collect()
// build completed foo with all partials
.map { partialArr in
var newFoo = foo
newFoo.partials = partialArr
return newFoo
}
}
.collect()