使用 Swift 和 Combine 链接 + 压缩多个网络请求

Posted

技术标签:

【中文标题】使用 Swift 和 Combine 链接 + 压缩多个网络请求【英文标题】:Chaining + zipping multiple network requests using Swift and Combine 【发布时间】:2021-10-23 02:47:17 【问题描述】:

总体用例是 4 个网络请求

A.请求 1 和 2 需要并行并等待两者 完成 乙。请求 3 在 1 和 2 完成后发生 C.请求 4 3 次完成后发生

C 之后的最终输出应该是AnyPublisher 类型

我正在尝试使用 Combine 来实现上述目标,到目前为止,我可以使用 Publishers.Zip 执行 A 和使用 flatMap 执行 C。我正在苦苦挣扎的是 B。我可以使用嵌套的完成处理程序来做到这一点,但不能使用 flatMap

这是带有字符串的示例代码(不是实际代码)

func overallFunc(arg1: "arg1", arg2: "arg2", arg3: "arg3", arg4: "arg4" ) -> AnyPublisher<String?, Error> 
let pub1 = func1(arg1: arg1, arg2: arg2)
let pub2 = func2(arg1: arg3, arg2: arg4)

let combinedPub = Publishers.Zip(pub1, pub2)

combinedPub
   .flatMap (response1, response2) in
     return func3(arg1: response1.attribute1, arg2: response2.attribute2)
   

func1func2func3 都返回 URLSession.shared.dataTaskPublisher,返回类型为 AnyPublisher&lt;String?, Error&gt;

现在我正在努力完成overallFunc 的代码。编译器在flatMap 周围给出以下错误。

Type of expression is ambiguous without more context

如果我在overallFunc 的最后一行添加一个额外的返回值,那么错误将变为 No 'flatMap' candidates produce the expected contextual result type 'AnyPublisher&lt;String?, Error&gt;'

简而言之,我想在 Publishers.Zip 的结果上使用 flatMap 并返回另一个发布者,以便我可以添加另一个 flatMap 来执行第四个请求,但我无法找出正确的语法和顺序要做的事情。

【问题讨论】:

尝试显式添加flatMap闭包参数的类型和返回类型,编译器应该会给你一个更好的错误信息。 我很确定您的返回类型不匹配。但是,查看所有方法的方法签名(即:func1/2/3)会有所帮助。您应该发布一个完整的示例。我也很困惑你打算从overallFunc 返回什么。联合酒吧?结合Pub.eraseToAnyPublisher()?等等 @Cristik 谢谢!添加返回类型有助于并提供更多有用的错误消息。 @Cristik 如果您提供您的评论作为答案,我可以接受它作为解决方案。 我只提出了一种调试技术,我觉得这不符合答案的条件。您应该添加一个答案,其中包含您所做的代码更改,并且可能在答案中提及评论对您有帮助。 【参考方案1】:

我认为您对代码进行混淆的尝试已经消除了您遇到的问题。以下代码编译得很好:

func func1(arg1: String, arg2: String) -> AnyPublisher<String?, Error>  fatalError() 
func func2(arg1: String, arg2: String) -> AnyPublisher<String?, Error>  fatalError() 
func func3(arg1: String, arg2: String) -> AnyPublisher<String?, Error>  fatalError() 
func func4(arg1: String) -> AnyPublisher<String?, Error>  fatalError() 

func overallFunc(arg1: String, arg2: String, arg3: String, arg4: String) -> AnyPublisher<String?, Error> 
    Publishers.Zip(
        func1(arg1: arg1, arg2: arg2),
        func2(arg1: arg3, arg2: arg4)
    )
        .flatMap  (response1, response2) in
            func3(arg1: response1 ?? "", arg2: response2 ?? "")
        
        .flatMap  response3 in
            func4(arg1: response3 ?? "")
        
        .eraseToAnyPublisher()

【讨论】:

对不起,下次会提供更具体的例子。谢谢你的回答!添加.eraseToAnyPublisher 有助于更好地隔离问题。解决它的方法是在 flatMap 闭包中显式添加返回类型。 啊,是的。如果你在 flatMap 中有不止一行代码,那么你通常必须定义返回类型(除非在闭包之外有一些东西可以让编译器清楚地知道返回类型应该是什么。)【参考方案2】:

flatMap 的结果将是 String?(请求 3 的结果),然后您希望将其传递给 mapmap 会将字符串转换为第四个请求。然后,您可以将订阅者添加到第四个请求,这将是您的整体序列的结果。所以...

pub1
  .zip(pub2)
  .flatMap  (response1, response2) in
     func3(arg1: response1.attribute1, arg2: response2.attribute2)
  
  .map 
     (func3Result : String?) in
      /* return publisher of 4th request */
  
  .eraseToAnyPublisher()

所有这一切的结果将是第四个请求的发布者。

【讨论】:

以上是关于使用 Swift 和 Combine 链接 + 压缩多个网络请求的主要内容,如果未能解决你的问题,请参考以下文章

Swift Combine - @Published 属性数组

如何使用 Combine + Swift 复制 PromiseKit 风格的链式异步流

如何在 Swift 中使用 Combine 读取 JSON 错误对象的属性值?

Swift Combine:没有“distinct”运算符?

使用 Swift Combine 创建一个 Timer Publisher

相当于在 Swift Combine 中使用 @Published 计算的属性?