Want to know for loop parallel using swift 5.5

Hello developers, I already see parallel code using constant number of parallel like this from official site.

async let firstPhoto = downloadPhoto(named: photoNames[0])
async let secondPhoto = downloadPhoto(named: photoNames[1])
async let thirdPhoto = downloadPhoto(named: photoNames[2])

let photos = await [firstPhoto, secondPhoto, thirdPhoto]
show(photos)

But I want to use this to variable size of works.

for photoName in photoNames {
    async let ... = downloadPhoto(named: photoName)
}
let photos = await ...
show(photos)

Does swift 5.5 support this case??? By any chance, do I use TaskGroup? I use TaskGroup already, but I want to know that swift supports it.

Answered by OOPer in 691535022

Seems you have read the Concurrency part of the Swift book, but it does not tell much about the current restrictions of async let.

Does swift 5.5 support this case???

If you mean using async let inside a code block of looping statement such as for-in, the answer is no.

In the accepted proposal SE-0317 async let bindings, you can find this description:

Specifically, async let declarations are not able to express dynamic numbers of tasks executing in parallel, like this group showcases:

func toyParallelMap<A, B>(_ items: [A], f: (A) async -> B) async -> [B] { 
  return await withTaskGroup(of: (Int, B).self) { group in
    var bs = [B?](repeating: nil, count: items.count)
    
    // spawn off processing all `f` mapping functions in parallel
    // in reality, one might want to limit the "width" of these
    for i in items.indices { 
      group.async { (i, await f(items[i])) }
    }
    
    // collect all results
    for await (i, mapped) in group {
      bs[i] = mapped
    }
    
    return bs.map { $0! }
  }
}

In the above toyParallelMap the number of child-tasks is dynamic because it depends on the count of elements in the items array at runtime. Such patterns are not possible to express using async let because we'd have to know how many async let declarations to create at compile time.


I use TaskGroup already, but I want to know that swift supports it.

Seems you need to go on with TaskGroup in cases such as described in your opening post.

Accepted Answer

Seems you have read the Concurrency part of the Swift book, but it does not tell much about the current restrictions of async let.

Does swift 5.5 support this case???

If you mean using async let inside a code block of looping statement such as for-in, the answer is no.

In the accepted proposal SE-0317 async let bindings, you can find this description:

Specifically, async let declarations are not able to express dynamic numbers of tasks executing in parallel, like this group showcases:

func toyParallelMap<A, B>(_ items: [A], f: (A) async -> B) async -> [B] { 
  return await withTaskGroup(of: (Int, B).self) { group in
    var bs = [B?](repeating: nil, count: items.count)
    
    // spawn off processing all `f` mapping functions in parallel
    // in reality, one might want to limit the "width" of these
    for i in items.indices { 
      group.async { (i, await f(items[i])) }
    }
    
    // collect all results
    for await (i, mapped) in group {
      bs[i] = mapped
    }
    
    return bs.map { $0! }
  }
}

In the above toyParallelMap the number of child-tasks is dynamic because it depends on the count of elements in the items array at runtime. Such patterns are not possible to express using async let because we'd have to know how many async let declarations to create at compile time.


I use TaskGroup already, but I want to know that swift supports it.

Seems you need to go on with TaskGroup in cases such as described in your opening post.

As your answer, I resolve this issue like this. This code download all web url file to local cache. And check total and completed number of them.

private static func downloadAllFilesAsync(_ medicalInfo: MedicalInfo)
  async throws -> (total: Int, completed: Int) {
...
    let download: (URL) async throws -> Int = { url in
      do {
        _ = try await WWW.downloadFileAsync(url)
        return 1
      } catch {
        throw error
      }
    }
     
    return try await withThrowingTaskGroup(of: Int.self) {
      group -> (Int, Int) in

      buildURLSet() // To be requested url array ([URL]) is set.
      let total = requested.count
      var completed = 0

      for requestUrl in requested {
        group.addTask {
          return try await download(requestUrl)
        }
      }
       
      completed = try await group.reduce(0, +)

      return (total, completed)
    }
}
Want to know for loop parallel using swift 5.5
 
 
Q