Swift 从对象数组映射到异步函数数组并等待它们全部
Swift map from array of objects to array of async functions and await them all
我刚刚更新到 Xcode 13.2.1,现在可以访问 async await,我正在尝试找到可以从 Combine “转换”到 async await 的地方。
我想实现以下...
给定一个类型...
struct Person {
let name: String
func fetchAvatar(completion: @escaping (UIImage?) -> Void) {
// fetch the image from the web and pass it into the completion.
}
}
我目前有这样的功能...
func fetchAllTheAvatars(people: [Person], completion: ([UIImage]) -> Void) {
Publisher.MergeMany(
people.map { person in
Future<UIImage?, Never> { promise in
person.fetchAvatar { promise(.success([=12=])) }
}
}
)
.compactMap { [=12=] }
.collect()
.sink { completion([=12=]) }
.store(in: &cancellables )
}
现在......在我看来这可能是转向使用异步等待的一个很好的候选者并且AsyncSequence
也许......?!?虽然它不一定是理想的,但我只是想了解如何使用它们。我习惯在 JS 和 TS 中使用 async await
,这似乎有点不同。 :D
我为我的 Person 添加了一个包装函数...
func fetchAvatar() async -> UIImage? {
await withCheckedContinuation { continuation in
fetchAvatar { image in
continuation.resume(returning: image)
}
}
}
但现在我被困在如何更新我的 fetchAllTheAvatars
功能上。
func fetchAllTheAvatars(people: [Person]) async -> [UIImage] {
people.map { ...???... }
}
我在网上看到的所有地方似乎都在使用 for await line in url.lines { ... }
,但我还没有 AsyncSequence。我需要以某种方式将 Person
的非异步数组“转换”为 () -> Image?
.
的 AsyncSequence
这可能吗?我这样做完全是错误的方式吗?
谢谢
标准模式是TaskGroup
。为单个图像添加任务,然后在 for
循环中 await
,map
,或者在本例中,reduce
:
func fetchAllTheAvatars(people: [Person]) async -> [Person.ID: UIImage] {
await withTaskGroup(of: (Person.ID, UIImage?).self) { group in
for person in people {
group.addTask { await (person.id, person.fetchAvatar()) }
}
return await group.reduce(into: [Person.ID: UIImage]()) { (dictionary, result) in
if let image = result.1 {
dictionary[result.0] = image
}
}
}
}
请注意,由于不能保证顺序,并且您的某些 Person
可能不是 return 图片,因此我的实施 return 是有效的,order-independent,结构(即字典)。
不用说了,上面假设你让Person
符合Identifiable
:
struct Person: Identifiable {
let id = UUID()
let name: String
func fetchAvatar() async -> UIImage? { … }
}
我刚刚更新到 Xcode 13.2.1,现在可以访问 async await,我正在尝试找到可以从 Combine “转换”到 async await 的地方。
我想实现以下...
给定一个类型...
struct Person {
let name: String
func fetchAvatar(completion: @escaping (UIImage?) -> Void) {
// fetch the image from the web and pass it into the completion.
}
}
我目前有这样的功能...
func fetchAllTheAvatars(people: [Person], completion: ([UIImage]) -> Void) {
Publisher.MergeMany(
people.map { person in
Future<UIImage?, Never> { promise in
person.fetchAvatar { promise(.success([=12=])) }
}
}
)
.compactMap { [=12=] }
.collect()
.sink { completion([=12=]) }
.store(in: &cancellables )
}
现在......在我看来这可能是转向使用异步等待的一个很好的候选者并且AsyncSequence
也许......?!?虽然它不一定是理想的,但我只是想了解如何使用它们。我习惯在 JS 和 TS 中使用 async await
,这似乎有点不同。 :D
我为我的 Person 添加了一个包装函数...
func fetchAvatar() async -> UIImage? {
await withCheckedContinuation { continuation in
fetchAvatar { image in
continuation.resume(returning: image)
}
}
}
但现在我被困在如何更新我的 fetchAllTheAvatars
功能上。
func fetchAllTheAvatars(people: [Person]) async -> [UIImage] {
people.map { ...???... }
}
我在网上看到的所有地方似乎都在使用 for await line in url.lines { ... }
,但我还没有 AsyncSequence。我需要以某种方式将 Person
的非异步数组“转换”为 () -> Image?
.
AsyncSequence
这可能吗?我这样做完全是错误的方式吗?
谢谢
标准模式是TaskGroup
。为单个图像添加任务,然后在 for
循环中 await
,map
,或者在本例中,reduce
:
func fetchAllTheAvatars(people: [Person]) async -> [Person.ID: UIImage] {
await withTaskGroup(of: (Person.ID, UIImage?).self) { group in
for person in people {
group.addTask { await (person.id, person.fetchAvatar()) }
}
return await group.reduce(into: [Person.ID: UIImage]()) { (dictionary, result) in
if let image = result.1 {
dictionary[result.0] = image
}
}
}
}
请注意,由于不能保证顺序,并且您的某些 Person
可能不是 return 图片,因此我的实施 return 是有效的,order-independent,结构(即字典)。
不用说了,上面假设你让Person
符合Identifiable
:
struct Person: Identifiable {
let id = UUID()
let name: String
func fetchAvatar() async -> UIImage? { … }
}