I can see Swift support 2 kinds of background task
Using DispatchQueue
DispatchQueue.global(qos: .background).async {}
Using Task
Task(priority: .background) {}
So I am confusing about what's the difference and which one should I use?
These are two completely different patterns (despite the superficially similar reference to a .background
QoS/priority). These two approaches are used in entirely different situations. This is a gross over-simplification, but:
The dispatch queue example is used in legacy, GCD codebases (those which have not (yet) adopted “Swift concurrency”) and you want to launch a unit of work that is, itself, synchronous but you want to avoid blocking the current thread; and
The Task {…}
is used in contemporary Swift concurrency codebases, generally to launch a task that, itself, consists of asynchronous code (notably, code that will await
some other async
method) … we use Task {…}
either to bridge from a synchronous context to an asynchronous one, or to enjoy some fine-grained control of unstructured concurrency (at the cost of greater complexity).
So, the question is more fundamental than DispatchQueue.global.async {…}
vs Task {…}
. The real question is GCD vs Swift concurrency. (And we almost always avoid intermixing GCD with Swift concurrency, except where absolutely necessary, but that is beyond the scope of this question. We would generally pick either GCD or Swift concurrency, and use that particular tech stack consistently within the given project.)
In contemporary codebases, we would adopt Swift concurrency (and thus favor Task {…}
, or, where you can, stick with structured concurrency), but if you are saddled with some legacy GCD codebase, then you might use global dispatch queues for the slow/synchronous work.
By the way, I would advise being a little more clear with the term “background task”. Specifically, I might encourage you to avoid conflating the term with a Swift concurrency TaskPriority
of .background
or a global dispatch queue with a QoSClass of .background
).
Traditionally, when we abstractly talk about a “background task” we mean “get it off the current thread”. But, the task “priority” and/or the dispatch queue “quality of service” is more of a question of the relative priority of this work with respect to other work also submitted.
Finally, it is worth stating that Task {…}
does not get work off the current context. In fact, quite the opposite is true: Task {…}
will run the work on behalf of the current actor. Using Task.detached {…}
is probably closer to the idea of a global dispatch queue, but even that is an imperfect analogy.
For more information, see The Swift Programming Language: Concurrency: Unstructured Concurrency or WWDC 2021 videos Meet async/await and Swift concurrency: Behind the scenes.