Combine中MergeMany+collect后erase报错及多数组发布者合并问题
我有两个返回Int数组的Deferred Publisher(底层为Future),希望它们并行运行后合并为一个Int数组(如p1返回的[1,2,3]与p2返回的[4,5,6]合并为[1,2,3,4,5,6]或[4,5,6,1,2,3])。尝试以下代码:
let p1: AnyPublisher<[Int], Never> let p2: AnyPublisher<[Int], Never> let combined: AnyPublisher<[Int], Never> = Publishers.MergeMany([p1, p2]) .collect() .eraseToAnyPublisher()
出现错误:Type of expression is ambiguous without a type annotation,collect后的类型为Publishers.Collect<Publishers.MergeMany<AnyPublisher<[Int], Never>>>,但无法擦除为目标类型。尝试assign方式也报错:Cannot convert value of type 'AnyPublisher<[Reminder], Never>' to expected argument type 'Published<[AnyPublisher<[Reminder], Never>.Output]>.Publisher'。后续用Zip3实现了需求,但该方案无法适配任意数量的发布者且存在额外存储开销,希望了解原方法报错原因及更通用的解决方案。
报错原因分析
核心问题是类型不匹配:
Publishers.MergeMany([p1, p2])的输出是单个的[Int]元素(每个发布者完成时发送一次自己的数组).collect()会把所有发送的[Int]收集成一个[[Int]](数组的数组),而你期望的最终类型是[Int],直接擦除会因类型不一致引发歧义报错。
通用解决方案
要实现任意数量发布者并行执行后合并为单个Int数组,只需在collect()之后添加一个扁平化步骤,把[[Int]]转成[Int]即可。有两种常见方式:
方式1:使用reduce拼接数组
let p1: AnyPublisher<[Int], Never> let p2: AnyPublisher<[Int], Never> let combined: AnyPublisher<[Int], Never> = Publishers.MergeMany([p1, p2]) .collect() .map { $0.reduce([], +) } // 将数组的数组拼接为单个数组 .eraseToAnyPublisher()
方式2:使用joined()转成序列再转数组
let p1: AnyPublisher<[Int], Never> let p2: AnyPublisher<[Int], Never> let combined: AnyPublisher<[Int], Never> = Publishers.MergeMany([p1, p2]) .collect() .map { Array($0.joined()) } // 扁平化序列并转为数组 .eraseToAnyPublisher()
这两种方式都支持任意数量的输入发布者,且并行执行(MergeMany本身就是并行订阅所有子发布者),最终输出合并后的单个Int数组,顺序取决于各发布者的完成顺序(符合你对合并顺序的要求)。
内容的提问来源于stack exchange,提问作者CalebK

