Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/114.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 组合框架:如何在继续之前异步处理数组的每个元素_Ios_Swift_Combine - Fatal编程技术网

Ios 组合框架:如何在继续之前异步处理数组的每个元素

Ios 组合框架:如何在继续之前异步处理数组的每个元素,ios,swift,combine,Ios,Swift,Combine,我在使用iOS Combine框架时遇到了一点心理障碍 我正在将一些代码从“手动”从远程API获取转换为使用Combine。基本上,API是SQL和REST(实际上是Salesforce,但这与问题无关)。代码用来调用一个REST查询方法,该方法接受一个完成处理程序。我正在做的是用联合收割机的未来来取代这一切。到目前为止,一切顺利 当以下情况发生时(并且经常发生),问题就会出现: 我们执行REST查询并返回一个“对象”数组 但这些“对象”并没有完全填充。它们中的每一个都需要来自某个相关对象的附加

我在使用iOS Combine框架时遇到了一点心理障碍

我正在将一些代码从“手动”从远程API获取转换为使用Combine。基本上,API是SQL和REST(实际上是Salesforce,但这与问题无关)。代码用来调用一个REST查询方法,该方法接受一个完成处理程序。我正在做的是用联合收割机的未来来取代这一切。到目前为止,一切顺利

当以下情况发生时(并且经常发生),问题就会出现:

  • 我们执行REST查询并返回一个“对象”数组

  • 但这些“对象”并没有完全填充。它们中的每一个都需要来自某个相关对象的附加数据。因此,对于每个“对象”,我们使用来自该“对象”的信息执行另一个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 
    
    执行此操作的旧代码看起来很糟糕,充满了嵌套的完成处理程序和由DispatchGroup
    enter
    /
    leave
    /
    notify
    保留的循环。但它奏效了。我不能让我的合并代码以同样的方式工作。我该怎么做?基本上,我的管道输出是一个数组,我觉得我需要将该数组拆分为单个元素,对每个元素异步执行一些操作,然后将这些元素重新组合到一个数组中。怎么做


    我解决这个问题的方法是可行的,但不能扩展,特别是当异步调用需要在管道链中返回几步的信息时。我一直在做这样的事情(我的想法来自):

  • 一组对象从上游到达

  • 我输入一个
    flatMap
    map
    将数组映射到一个发布者数组,每个发布者都有一个未来,它获取与一个对象相关的更多在线内容,然后是一个生成修改对象的管道

  • 现在我有了一个管道阵列,每个管道生成一个对象。我
    merge
    该数组并从
    flatMap
    生成该发布者(MergeMany)

  • I
    将结果值收集回数组中

  • 但这似乎仍然需要大量的工作,更糟糕的是,当每个子管道本身需要生成一系列子管道时,它无法扩展。这一切都变得不可理解,过去容易到达完成块的信息(因为Swift的范围规则)不再到达主管道中的后续步骤(或者因为我在管道中传递越来越大的元组而难以到达)


    必须有一些简单的组合模式来做这件事,但我完全错过了。请告诉我它是什么。

    使用公认的答案,我总结了以下结构:

    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
    循环。联合收割机当量为:

    • 行中…的
      等效的是
      flatMap
      和publisher.Sequence

    • 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()