Skip to content

Async/Await#515

Merged
kean merged 9 commits into
masterfrom
async-await
Dec 27, 2021
Merged

Async/Await#515
kean merged 9 commits into
masterfrom
async-await

Conversation

@kean

@kean kean commented Oct 24, 2021

Copy link
Copy Markdown
Owner

1. Add ImagePipeline loadImage async method

do {
    return try await pipeline.loadImage(with: request)
} catch {
    guard let error = (error as? ImagePipeline.Error),
          (error.dataLoadingError as? URLError)?.networkUnavailableReason == .constrained else {
        throw error
    }
    return try await pipeline.loadImage(with: lowQualityImageURL)
}

2. FetchImage now supports async responses

struct ImageView: View {
    let url: URL

    @StateObject private var image = FetchImage()

    var body: some View {
        ZStack {
            Rectangle().fill(Color.gray)
            image.view?
                .resizable()
                .aspectRatio(contentMode: .fill)
                .clipped()
        }
        .onAppear { loadImage(url) }
        .onChange(of: url) { loadImage($0) }
        .onDisappear(perform: image.reset)
    }

    private func loadImage(url: URL) {
         image.load {
               try await ImagePipeline.shared.loadImage(with: url) // Or could be any async task you want
         }
    }
}

Comment on lines +150 to +167
final class TaskBox {
// TODO: Does it need thread-safety?
// TODO: Are we sure loadImage will call again every time you await?
var task: ImageTask?
}
let box = TaskBox()
return try await withTaskCancellationHandler(handler: {
// TODO: How can we report cancellation from here? Add cancellation to ImageTask?
box.task?.cancel()
}, operation: {
try await withUnsafeThrowingContinuation { continuation in
box.task = self.loadImage(with: request, queue: nil, progress: nil) {
continuation.resume(with: $0)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, you would make ImageTask conform to Sendable and then use it like this:

Suggested change
final class TaskBox {
// TODO: Does it need thread-safety?
// TODO: Are we sure loadImage will call again every time you await?
var task: ImageTask?
}
let box = TaskBox()
return try await withTaskCancellationHandler(handler: {
// TODO: How can we report cancellation from here? Add cancellation to ImageTask?
box.task?.cancel()
}, operation: {
try await withUnsafeThrowingContinuation { continuation in
box.task = self.loadImage(with: request, queue: nil, progress: nil) {
continuation.resume(with: $0)
}
}
var task: ImageTask?
return try await withTaskCancellationHandler(handler: { [task] in
// TODO: How can we report cancellation from here? Add cancellation to ImageTask?
task?.cancel()
}, operation: {
try await withUnsafeThrowingContinuation { continuation in
task = self.loadImage(with: request, queue: nil, progress: nil) {
continuation.resume(with: $0)
}
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea, thanks! Initially, I just went with what the compiler suggested. I'm planning to wrap this up today.

@kean kean Nov 4, 2021

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem that I'll be able to make ImageTask Sendable. It's not thread-safe and has to be thread-confined.

@kean kean changed the title Draft: Async/Await Async/Await Dec 27, 2021
@kean kean merged commit 932d5fc into master Dec 27, 2021
@kean kean deleted the async-await branch December 27, 2021 12:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants