flatMap

Combine 框架中,flatMap 是一个非常强大的操作符,用于将输入 Publisher 发出的每个值转换为另一个 Publisher,并将这些内层 Publisher 的输出扁平化成一个单一的流。flatMap 适用于需要对每个输入值进行异步操作或者产生多个输出值的场景。

基本用法

flatMap 的基本签名如下:

func flatMap<NewPublisher: Publisher>(
    _ transform: @escaping (Output) -> NewPublisher,
    maxPublishers: Subscribers.Demand? = nil
) -> Publishers.FlatMap<Self, NewPublisher>
  • transform: 一个闭包,接收输入值并返回一个新的 Publisher

  • maxPublishers: 可选参数,用于限制同时活动的内层 Publisher 的数量。

示例

以下是一个简单的示例,展示如何使用 flatMap 将一个整数转换为一个 Publisher,并将结果扁平化为一个单一流:

import Combine

let numbers = [1, 2, 3].publisher

let flatMapSubscription = numbers
    .flatMap { number in
        Just(number * 2) // 将每个数字转换为新的 Publisher
    }
    .sink { value in
        print(value) // 输出: 2, 4, 6
    }

在这个示例中:

  • 我们创建了一个包含整数的 Publisher

  • 使用 flatMap 将每个数字乘以 2,并将结果封装在 Just 中返回一个新的 Publisher

  • sink 接收并打印最终结果。

处理异步操作

flatMap 还可以用于处理异步操作,比如网络请求。以下是一个处理异步请求的示例:

import Combine

func fetchData(for number: Int) -> AnyPublisher<String, Never> {
    // 模拟一个网络请求,返回一个 Publisher
    let data = "Fetched data for number \(number)"
    return Just(data)
        .delay(for: .seconds(1), scheduler: RunLoop.main) // 模拟延迟
        .eraseToAnyPublisher()
}

let numbersForAsync = [1, 2, 3].publisher

let asyncSubscription = numbersForAsync
    .flatMap { number in
        fetchData(for: number) // 对每个数字进行网络请求
    }
    .sink { value in
        print(value) // 输出: Fetched data for number 1, Fetched data for number 2, Fetched data for number 3
    }

在这个示例中:

  • fetchData(for:) 函数模拟一个网络请求,返回一个 Publisher,其中包含从网络获取的数据。

  • 使用 flatMap 对每个数字进行网络请求,并将结果扁平化为一个流。

  • sink 接收并打印网络请求的结果。

错误处理

如果需要处理可能发生的错误,可以使用 flatMapcatch 操作符结合使用:

import Combine

func fetchDataWithError(for number: Int) -> AnyPublisher<String, Error> {
    if number == 2 {
        return Fail(error: NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Error for number 2"]))
            .eraseToAnyPublisher()
    }
    let data = "Fetched data for number \(number)"
    return Just(data)
        .delay(for: .seconds(1), scheduler: RunLoop.main)
        .setFailureType(to: Error.self)
        .eraseToAnyPublisher()
}

let numbersWithError = [1, 2, 3].publisher

let errorHandlingSubscription = numbersWithError
    .flatMap { number in
        fetchDataWithError(for: number)
            .catch { error in
                Just("Error occurred: \(error.localizedDescription)") // 处理错误并返回默认值
            }
    }
    .sink { value in
        print(value) // 输出: Fetched data for number 1, Error occurred: Error for number 2, Fetched data for number 3
    }

在这个示例中:

  • fetchDataWithError(for:) 函数模拟一个可能会抛出错误的网络请求。

  • 使用 flatMap 对每个数字进行请求,并在发生错误时使用 catch 来处理。

  • sink 接收并打印结果,包括错误处理的输出。

总结

  • flatMap 是一个用于将输入值转换为内层 Publisher 并扁平化的操作符。

  • 它适用于处理异步操作,如网络请求或需要多个输出的场景。

  • 可以与错误处理结合使用,以便在流中捕获并处理错误。

  • flatMap 使得组合和处理复杂的数据流变得更加灵活和强大。

Last updated