2

The following code can be found at https://www.swiftbysundell.com/articles/async-and-concurrent-forEach-and-map/. The concurrentMap function is the concurrent version of map. It executes multiple operations in parallel and returns the results in the original order.

extension Sequence {
    func asyncMap<T>(
        _ transform: (Element) async throws -> T
    ) async rethrows -> [T] {
        var values = [T]()

        for element in self {
            try await values.append(transform(element))
        }

        return values
    }

    func concurrentMap<T>(
        _ transform: @escaping (Element) async throws -> T
    ) async throws -> [T] {
        let tasks = map { element in
            Task {
                try await transform(element)
            }
        }

        return try await tasks.asyncMap { task in
            try await task.value
        }
    }
}

I don't understand how the concurrentMap function works. Why is it executed in parallel? In the asyncMap, it does try await values.append(transform(element)), so it looks like it waits for each process. But in fact they are executed in parallel.

I have confirmed that the concurrentMap function is working correctly, but I don't understand why this is happening, so I would appreciate an explanation.

user9015472
  • 157
  • 1
  • 8

1 Answers1

2

Tasks are executed immediately after creation and do not require explicit initiation. Therefore, each process is executed (concurrently) at the point where tasks are created.

I misunderstood that it would not be executed until the declaration of .value.
value | Apple Developer Documentation

user9015472
  • 157
  • 1
  • 8
  • 1
    I’ve up-voted, but as a refinement, I would advise avoiding the term “in parallel”. This does not *not* execute in “parallel”. They run “concurrently”. See [What is the difference between concurrency and parallelism?](https://stackoverflow.com/a/1050257/1271826) – Rob Dec 30 '22 at 16:16
  • @Rob Thank you! Just checking. Swift Concurrency allows us to run tasks concurrently. And if the system has sufficient resources, tasks can be executed in parallel. Is that correct? – user9015472 Dec 31 '22 at 06:13
  • 1
    Parallelism is more of a question of design than resources. It comes down to how you create these separate tasks. To achieve true parallelism, you have to use detached tasks, task groups, or distinct actors. But `Task { … }` runs on the current actor. The whole M.O. of actors is thread-safety achieved by only one thing running at a time on the actor at a time (but obviously when you encounter an `await`, that task is suspended, freeing another `Task` on the same actor to now run). An actor uses concurrency through by interleaving, but only running one at any given moment in time. – Rob Dec 31 '22 at 06:42
  • 1
    See https://gist.github.com/robertmryan/6ce0190fbb77362b510a1147cb0e6fda for example of parallel map approach in Swift concurrency. As you can see, given that offers true parallelism, then you are now stuck collating the results (which may finish in any order) back in their original order. Thus, Sundell’s example launches concurrent tasks, but awaits them in order. That gist launches parallel tasks, awaits them in whatever order they actually finish, and you have to reorganize the results back in order. – Rob Dec 31 '22 at 06:46