Swift中如何在Array的compactMap/forEach里调用异步方法?
解决方案
针对你的需求,有两种简洁的方式实现异步版本的compactMap,替代传统的for循环:
1. 顺序执行的异步compactMap(与原for循环逻辑一致)
给Array添加一个异步扩展方法,内部逻辑和你写的for循环完全相同,封装成可复用的工具方法:
extension Array { func compactMap<T>(_ transform: (Element) async throws -> T?) async rethrows -> [T] { var result = [T]() for element in self { if let transformed = try await transform(element) { result.append(transformed) } } return result } }
使用方式和同步compactMap几乎一致,直接在异步上下文调用即可:
// 在你的Task闭包的do块中添加 let newObjs2 = try await providers.compactMap { provider in try await MyClass.create(withItemProvider: provider) } print("newObjs2: \(newObjs2)")
这个方法会按顺序处理每个NSItemProvider,和原for循环的执行逻辑、结果顺序完全一致。
2. 并行执行的异步转换(提升效率)
如果不需要严格保持原数组的处理顺序,或者希望同时处理多个NSItemProvider以提高效率,可以用TaskGroup实现并行转换:
// 在你的Task闭包的do块中添加 let newObjs3 = try await withThrowingTaskGroup(of: MyClass?.self) { group in // 给每个provider分配一个异步任务 for provider in providers { group.addTask { try await MyClass.create(withItemProvider: provider) } } // 收集所有任务结果并过滤nil var result = [MyClass]() for try await obj in group { if let obj = obj { result.append(obj) } } return result } print("newObjs3: \(newObjs3)")
如果需要保持原数组的顺序,可以给任务带上索引,最后按索引重组结果:
let newObjs4 = try await withThrowingTaskGroup(of: (Int, MyClass?).self) { group in for (index, provider) in providers.enumerated() { group.addTask { (index, try await MyClass.create(withItemProvider: provider)) } } var result = [MyClass?](repeating: nil, count: providers.count) for try await (index, obj) in group { result[index] = obj } return result.compactMap { $0 } } print("newObjs4: \(newObjs4)")
为什么原compactMap/forEach无法使用?
标准库中的compactMap、forEach都是同步方法,它们要求传入的闭包是同步执行的(不能包含async/await)。而你的转换逻辑依赖异步函数,直接传入异步闭包会触发类型不匹配的错误。
内容的提问来源于stack exchange,提问作者HangarRash
相关产品推荐
相关产品推荐

