Greetings, traveler!
In the last article, we discussed the basics of async/await and settled on Tasks. Tasks allow you to perform various operations in an asynchronous context simultaneously. You can place calls to asynchronous methods inside a single task or split them into several.
In that article, we considered this use case.
func loadImage(url: URL) async throws -> UIImage {
let request = URLRequest(url: url)
let (data, _) = try await URLSession.shared.data(for: request)
guard let image = UIImage(data: data) else {
throw ImageError.invalidData
}
return image
}
Task {
do {
let image = try await loadImage(url: URL(string: "https://picsum.photos/200/300")!)
} catch {
print(error)
}
}
Task priority
We can specify a priority for a Task by choosing from six modes:
- high
- medium
- low
- userInitiated
- utility
- background
The default priority is medium. The lowest priority is the background, where the task can perform operations in the background. This execution will be slower than the other modes but will not affect the user experience by slowing down the rest of the application modules. If we need to increase the priority, we can specify that the Task runs in userInitiated mode. This mode has a higher execution speed. It is assumed that the user is aware of it and is waiting for its completion.
var images: [Int: UIImage] = [
0: UIImage(systemName: "Home")!,
1: UIImage(systemName: "Heart")!
]
func getImage(id: Int) async -> UIImage? {
images[id]
}
func getImage() {
Task(priority: .userInitiated) {
let image = await getImage(id: 0)
}
}
Parallel execution
We can wrap several asynchronous functions into several tasks. They will be executed in parallel, while synchronous code outside the task context will executed sequentially.
Task {
let image = await getImage(id: 0)
}
Task {
let image = await getImage(id: 1)
}
Asynchronous code within a single Task can be executed sequentially or in parallel. This is what the sequentially executed code will look like.
func getImage(id: Int) async -> UIImage? {
print("Image id: \(id)")
return images[id]
}
func getImages() {
Task {
let firstImage = await getImage(id: 0)
let secondImage = await getImage(id: 1)
let images = [firstImage, secondImage]
}
}
Since we added print to the body of our function, we can see what it will print to the console.
Image id: 0
Image id: 1
As you can see, the execution of each subsequent function was waiting for the completion of the previous one. But you can run these operations in parallel to achieve better performance. To do this, we will need the async let tool. I suggest reading about it in the following article.