Running tasks in parallel with async-await in Swift part 2

In the previous blog post Running tasks in parallel with async-await in Swift we looked at using TaskGroup for running multiple tasks in parallel. The benefit of TaskGroup is that we can create n number of tasks easily by, for example, iterating an array and creating a task for each of the element. Moreover, groups provide control over cancellation and prioritization. Another way of running tasks in parallel is using the async let syntax. This is useful when we have a fixed number of tasks what we want to run in parallel. Let’s take a look at an example where we need to fetch two images and later merge two images into one. We can imagine it is some sort of image editing app.

@MainActor final class ViewModel: ObservableObject {
func prepare() {
Task {
do {
async let wallpaperData = service.fetchImageData(id: "blue_wallpaper")
async let overlayData = service.fetchImageData(id: "gradient_overlay")
self.backgroundImage = try await ImageProcessor.merge(background: wallpaperData, overlay: overlayData)
} catch {
// TODO: present/handle error
struct ImageProcessor {
static func merge(background: Data, overlay: Data) throws -> UIImage {
view raw ViewModel.swift hosted with ❤ by GitHub

In the snippet above, we have a prepare function which is triggered by some user interface event, let’s say a button tap. What happens first is that we create a Task which captures the async work we want to do and starts running immediately. The task also catches any errors, but we haven’t filled in what to do with the error. By using the async let syntax, we can start fetching images in parallel. Note that if we would not use async keyword before the let here we would need to add await before the fetch calls and in that case images are fetched serially. Nice thing about the async let is that function arguments defined by the ImageProcessor do not need any special treatment. Swift compiler just requires to call the next throwing non-async function with try await to wait for both fetch tasks before they are passed into the merge function. Therefore, it is easy to use async let since it does not have any other implication to the following code except just requiring to use await keyword.

If this was helpful, please let me know on Twitter @toomasvahter. Feel free to subscribe to RSS feed. Thank you for reading.