What’s the difference between async let, tasks, and task groups?
What’s the difference between async let, tasks, and task groups? 관련
Updated for Xcode 15
Swift’s async let
, Task
, and task groups all solve a similar problem: they allow us to create concurrency in our code so the system is able to run them efficiently. Beyond that, the way they work is quite different, and which you’ll choose depends on your exact scenario.
To help you understand how they differ, and provide some guidance on where each one is a good idea, I want to walk through the key behaviors of each of them.
First, async let
and Task
are designed to create specific, individual pieces of work, whereas task groups are designed to run multiple pieces of work at the same time and gather the results. As a result, async let
and Task
have no way to express a dynamic amount of work that should run in parallel.
For example, if you had an array of URLs and wanted to fetch them all in parallel, convert them into arrays of weather readings, then average them to a single Double
, task groups would be a great choice because you won’t know ahead of time how many URLs are in your array. Trying to write this using async let
or Task
just wouldn’t work, because you’d have to hard-code the exact number of async let
lines rather than just loop over an array.
Second, task groups automatically let us process results from child tasks in the order they complete, rather than in an order we specify. For example, if we wanted to fetch five pieces of data, task groups allow us to use group.next()
to read whichever of the five comes back first, whereas using async let
and Task
would require us to await values in a specific, fixed order.
That alone is a helpful feature of task groups, but in some situations it goes from helpful to crucial. For example, if you have three possible servers for some data and want to use whichever one responds fastest, task groups are perfect – you can use addTask()
once for each server, then call next()
only once to read whichever one responded fastest.
Third, although all three forms of concurrency will automatically be marked as cancelled if their parent task is cancelled, only Task
and task group can be cancelled directly, using cancel()
and cancelAll()
respectively. There is no equivalent for async let
.
Fourth, because async let
doesn’t give us a handle to the underlying task it creates for us, it’s not possible to pass that task elsewhere – we can’t start an async let
task in one function then pass that task to a different function. On the other hand, if you create a task that returns a string and never throws an error, you can pass that Task<String, Never>
object around as needed.
And finally, although task groups can work with heterogeneous results – i.e., child tasks that return different types of data – it takes the extra work of making an enum to wrap the data. async let
and Task
do not suffer from this problem because they always return a single result type, so each result can be different.
By sheer volume of advantages you might think that async let
is clearly much less useful than both Task
and task groups, but not all those points carry equal weight in real-world code. In practice, I would suggest you’re likely to:
- Use
async let
the most; it works best when there is a fixed amount of work to do. - Use
Task
for some places whereasync let
doesn’t work, such as passing an incomplete value to a function. - Use task groups least commonly, or at least use them directly least commonly – you might build other things on top of them.
I find that order is pretty accurate in practice, for a number of reasons:
- I normally want results from all the work I start, so being able to skip some or get results in completion order is less important.
- It’s surprisingly common to want to work with different data types, which is clumsy with task groups.
- If I need to be able to cancel tasks,
Task
is similar enough toasync let
that it’s easy to move across toTask
without going all the way to a task group.
So, again I would recommend you start with async let
, move to Task
if needed, then go to task groups only if there’s something specific they offer that you need.