英文:
Dealing With Errors in a Throwing taskGroup
问题
I'm using a withThrowingTaskGroup to iterate through an array of remote URLs, downloading the images and saving them to disk.
private func batchDownloadImages(from remoteImages: [RemoteImageData]) async throws -> [SampleImage]{
try await withThrowingTaskGroup(of: SampleImage.self) { group in
for image in remoteImages {
group.addTask(priority: .userInitiated) {
return try await self.downloadFile(from: image)
}
}
return try await group.reduce(into: [SampleImage]()) { partialResult, name in
partialResult.append(name)
}
}
}
downloadFile(from:) is responsible for downloading the file from the URL and saving it to disk. It will throw an error either when the download fails or when the disk operation fails.
In my current code, if one download fails then all tasks are canceled, and the successful downloads aren't saved to disk.
If an error is thrown because the download fails, I want to ignore it and let all other tasks run as normal (as that one URL might have issues). However, if it fails because I can't save it, I want to deal with that error and cancel all other tasks. Is this possible?
英文:
I'm using a withThrowingTaskGroup to iterate through an array of remote URLs, downloading the images and saving them to disk.
private func batchDownloadImages(from remoteImages: [RemoteImageData]) async throws -> [SampleImage]{
try await withThrowingTaskGroup(of: SampleImage.self) { group in
for image in remoteImages {
group.addTask(priority: .userInitiated) {
return try await self.downloadFile(from: image)
}
}
return try await group.reduce(into: [SampleImage]()) { partialResult, name in
partialResult.append(name)
}
//return []
}
downloadFile(from:) is responsible for downloading the file from the URL and saving it to disk. It will throw an error either when the download fails or when the disk operation fails.
In my current code, if one download fails then all tasks are canceled and the successful downloads rent saved to disk.
If an error is thrown because the download fails I want to ignore it and let all other tasks run as normal (as that one URL might have issues) however if it fails because I can't save it I want to deal with that error and cancel all other tasks. Is this possible?
答案1
得分: 3
你可以捕获下载失败时抛出的错误,仅限该错误。
假设 downloadFile
大致如下:
func downloadFile(from: RemoteImageData) async throws -> SampleImage {
let image: SampleImage
do {
image = try downloadImage()
} catch {
throw MyError.downloadFailed(error)
}
do {
try saveImageToFile(image)
} catch {
throw MyError.saveFailed(error)
}
return image
}
enum MyError: Error {
case downloadFailed(Error)
case saveFailed(Error)
}
然后,你可以专门捕获 MyError.downloadFailed
:
// 请注意,任务组的类型已更改为 Result<SampleImage, MyError>,
// 以包含被抛出的下载错误
try await withThrowingTaskGroup(of: Result<SampleImage, MyError>.self) { group in
for image in remoteImages {
group.addTask(priority: .userInitiated) {
do {
return Result.success(try await downloadFile(from: image))
} catch let MyError.downloadFailed(error) {
return Result.failure(MyError.downloadFailed(error))
}
}
}
return try await group.reduce(into: [SampleImage]()) { partialResult, name in
switch name {
case let .success(image):
partialResult.append(image)
case let .failure(error):
print(error) // 以某种方式处理此错误
}
}
}
实际上,所有的下载错误将在 reduce
中被捕获和处理,而所有的文件保存错误将像以前一样被抛出。
英文:
You can catch the error that gets thrown when the download fails, and only that error.
Suppose downloadFile
is something like this:
func downloadFile(from: RemoteImageData) async throws -> SampleImage {
let image: SampleImage
do {
image = try downloadImage()
} catch {
throw MyError.downloadFailed(error)
}
do {
try saveImageToFile(image)
} catch {
throw MyError.saveFailed(error)
}
return image
}
enum MyError: Error {
case downloadFailed(Error)
case saveFailed(Error)
}
Then you can catch specifically MyError.downloadFailed
:
// Note that the type of the task group has been changed to Result<SampleImage, MyError>
// to include the download errors that gets thrown
try await withThrowingTaskGroup(of: Result<SampleImage, MyError>.self) { group in
for image in remoteImages {
group.addTask(priority: .userInitiated) {
do {
return Result.success(try await downloadFile(from: image))
} catch let MyError.downloadFailed(error) {
return Result.failure(MyError.downloadFailed(error))
}
}
}
return try await group.reduce(into: [SampleImage]()) { partialResult, name in
switch name {
case let .success(image):
partialResult.append(image)
case let .failure(error):
print(error) // handle this in some way
}
}
}
Effectively, all the download errors will be caught and handled in the reduce
, and all the file-saving errors will be thrown, like it was before.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论