处理抛出错误的任务组(TaskGroup)中的错误。

huangapple go评论66阅读模式
英文:

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 -&gt; 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&lt;SampleImage, MyError&gt;
// to include the download errors that gets thrown
try await withThrowingTaskGroup(of: Result&lt;SampleImage, MyError&gt;.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.

huangapple
  • 本文由 发表于 2023年5月15日 11:14:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76250666.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定