You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Combine对数组中每个对象发起多个API请求?

问题描述

我调用一个函数得到了类型为AnyPublisher<[Object], Error>的结果,现在需要遍历数组里的每个对象,用单个对象的属性发起至少3个API请求(异步同步均可),再结合原对象与各API返回的对象创建新对象并发布。

流程如下:

AnyPublisher<[Object], Error>
|
遍历对象数组
|
利用对象的某个属性发起3个API请求
|
结合原对象与API返回对象,创建并发布新对象

请问该怎么实现?

另外我写了一段测试代码,是基于另一篇帖子的代码修改的,但它在内部flatMap完成后就执行map,而不是收集所有发布者后再执行map。请问我哪里错了?

代码如下:

struct FoodDetailInput {
    let appear: AnyPublisher<Void, Never>
}

let appearRecord = PassthroughSubject<Void, Never>()// 进入详情页时触发
let input = FoodDetailInput(appear: appearRecord.eraseToAnyPublisher())

let foodDetails = input.appear
.flatMap({[unowned self] _ in self.dataProvider.ingredients(for: food.id) })
.flatMap({ ingredients -> AnyPublisher<Ingredient, Error> in
    Publishers.Sequence(sequence: ingredients).setFailureType(to: Error.self).eraseToAnyPublisher()
})
.flatMap({[unowned self] singleIngredient  in
    self.dataProvider.variants(for: food.id, ingredientName: singleIngredient.id)
        .flatMap { variants -> AnyPublisher<DetailPageObject, Error> in
            var singleDetailObject = DetailPageObject(ingredient: singleIngredient)
            singleDetailObject.variants = variants
            return Just(singleDetailObject).setFailureType(to: Error.self).eraseToAnyPublisher()
        }
        .collect()
        .map { detailPageObjects -> PageDetailsState  in
            PageDetailsState.success(detailPageObjects)
        }
})
.replaceError(with:PageDetailsState.success([]))
.eraseToAnyPublisher()

代码问题分析

  1. collect()位置错误:你把collect()放在了单个singleIngredient的flatMap内部,这会导致每个Ingredient对应的请求完成后立刻收集并转换为PageDetailsState,而非等所有Ingredient的请求都结束后统一收集结果。
  2. 嵌套flatMap逻辑混乱:外层已经将ingredients数组拆分为单个Ingredient的流,内层又做了不必要的flatMap,导致流的层级混乱,无法实现“统一收集所有结果”的目标。

正确实现思路与代码

核心逻辑步骤

  1. 将数组流拆分为单个元素的流;
  2. 对每个元素发起多个API请求,等待所有请求完成后合并原元素与请求结果;
  3. 收集所有处理后的新对象,统一发布最终结果。

通用场景示例代码

// 假设基础类型定义
struct Object {
    let id: String
    // 其他属性
}

struct ApiResult1 { /* API1返回类型 */ }
struct ApiResult2 { /* API2返回类型 */ }
struct ApiResult3 { /* API3返回类型 */ }

struct NewObject {
    let original: Object
    let result1: ApiResult1
    let result2: ApiResult2
    let result3: ApiResult3
}

// 初始发布者(你已有的AnyPublisher<[Object], Error>)
let initialPublisher: AnyPublisher<[Object], Error> = getDataProvider()

let finalPublisher = initialPublisher
    // 1. 把数组拆成单个Object的流
    .flatMap { objects -> AnyPublisher<Object, Error> in
        Publishers.Sequence(sequence: objects)
            .setFailureType(to: Error.self)
            .eraseToAnyPublisher()
    }
    // 2. 对每个Object发起3个API请求,等待全部完成后合并结果
    .flatMap { object -> AnyPublisher<NewObject, Error> in
        let api1 = dataProvider.fetchApi1(for: object.id)
        let api2 = dataProvider.fetchApi2(for: object.id)
        let api3 = dataProvider.fetchApi3(for: object.id)
        
        // 用Zip3组合三个请求,必须全部完成才会触发后续逻辑
        return Publishers.Zip3(api1, api2, api3)
            .map { result1, result2, result3 in
                NewObject(original: object, result1: result1, result2: result2, result3: result3)
            }
            .eraseToAnyPublisher()
    }
    // 3. 收集所有处理后的NewObject,转换成数组发布
    .collect()
    .replaceError(with: [])
    .eraseToAnyPublisher()

针对你提供的FoodDetail场景的修正代码

let foodDetails = input.appear
    // 获取ingredients数组
    .flatMap { [unowned self] in
        self.dataProvider.ingredients(for: food.id)
    }
    // 拆成单个Ingredient的流
    .flatMap { ingredients -> AnyPublisher<Ingredient, Error> in
        Publishers.Sequence(sequence: ingredients)
            .setFailureType(to: Error.self)
            .eraseToAnyPublisher()
    }
    // 对每个Ingredient获取variants并创建DetailPageObject
    .flatMap { [unowned self] singleIngredient -> AnyPublisher<DetailPageObject, Error> in
        self.dataProvider.variants(for: food.id, ingredientName: singleIngredient.id)
            .map { variants in
                var detailObj = DetailPageObject(ingredient: singleIngredient)
                detailObj.variants = variants
                return detailObj
            }
            .eraseToAnyPublisher()
    }
    // 收集所有DetailPageObject,统一转换成PageDetailsState
    .collect()
    .map { PageDetailsState.success($0) }
    .replaceError(with: PageDetailsState.success([]))
    .eraseToAnyPublisher()

关键修正点

  1. 将collect()移到最外层流中,确保等待所有Ingredient的请求完成后再统一收集结果;
  2. 移除不必要的内层flatMap,直接用map转换variants为DetailPageObject,简化流的层级;
  3. 保证每个Ingredient的处理逻辑独立,最终统一输出合并后的状态。

内容的提问来源于stack exchange,提问作者wolverine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 13:18:09