3-15.【函数式编程】为什么纯函数更容易与 async/await、Combine 或 Swift Concurrency 集成。

4 阅读2分钟

1️⃣ 核心原因

纯函数的特性使它们在异步编程中非常自然地组合和使用:

纯函数特性对异步编程的优势
输入决定输出异步任务可以安全地并行执行,不依赖外部状态,保证相同输入得到相同输出
无副作用异步操作不会意外修改共享状态,避免 race condition 或数据冲突
可组合性可以安全地链式组合异步操作,如 map、flatMap、reduce 等,保持逻辑纯净
易测试异步逻辑中的核心计算可独立测试,无需 mock I/O 或线程状态

2️⃣ async/await 场景

示例:纯函数 + 异步网络

struct User: Decodable {
    let id: Int
    let name: String
}

// 纯函数:解析数据
func parseUser(data: Data) throws -> User {
    return try JSONDecoder().decode(User.self, from: data)
}

// 异步函数:获取网络数据
func fetchUserData(id: Int) async throws -> Data {
    let url = URL(string: "https://api.example.com/user/(id)")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}

// 流水线组合
func fetchAndParseUser(id: Int) async throws -> User {
    let data = try await fetchUserData(id: id)   // 副作用在这里
    return try parseUser(data: data)             // 纯函数
}

优势

  • parseUser 是纯函数,可单元测试
  • 网络请求是副作用,集中在 async/await 调用层
  • 逻辑清晰,副作用可控

3️⃣ Combine 场景

示例:数据处理流水线

import Combine
import Foundation

let url = URL(string: "https://api.example.com/users")!

// 纯函数:解析 JSON
func decodeUsers(data: Data) -> [User] {
    (try? JSONDecoder().decode([User].self, from: data)) ?? []
}

// Combine 流水线
let cancellable = URLSession.shared.dataTaskPublisher(for: url)
    .map { $0.data }           // 副作用在 URLSession
    .map(decodeUsers)          // 纯函数
    .sink(receiveCompletion: { completion in
        print("Done")          // 副作用
    }, receiveValue: { users in
        print("Users count:", users.count) // 副作用
    })

优势

  • decodeUsers 是纯函数 → 安全组合
  • 异步、网络请求在 Publisher 层 → 副作用集中
  • 支持链式操作(map、flatMap、filter)而不会破坏数据可预测性

4️⃣ Swift Concurrency 场景

纯函数在 Swift Concurrency(actors、Task、async/await)中天然安全:

  • 纯函数不访问共享状态 → 可以跨 Task 并发调用
  • 不需要加锁或 actor 封装
  • 易于组合异步流水线
func processNumbers(_ nums: [Int]) async -> [Int] {
    return await withTaskGroup(of: Int.self) { group in
        for n in nums {
            group.addTask {
                return n * n // 纯函数
            }
        }
        var results = [Int]()
        for await r in group {
            results.append(r)
        }
        return results
    }
}
  • 并发任务中使用纯函数,线程安全
  • 逻辑清晰,副作用(如打印)可以在最后集中执行

5️⃣ 总结

特性为什么适合异步编程
输入决定输出可并行执行,无需关心共享状态
无副作用不需要锁或同步机制,天然线程安全
可组合可以与 async/await、Task、Publisher 链式组合
可测试异步逻辑核心可独立测试,I/O 副作用隔离
可预测异步环境中减少 race condition 和不可控状态问题

💡 经验法则

在 Swift 异步编程中,把数据处理、计算逻辑写成纯函数,把 I/O、网络请求、打印等副作用集中在 Task/Publisher/async 层,保证安全、可组合、易维护。