First of all, it is important to note that neither of these create child tasks. A child task is created by structured concurrency constructs like await
ing an async
function, async let
, or task groups, as opposed to unstructured concurrency like Task.init
and Task.detached
.
In addition to the structured approaches to concurrency described in the previous sections, Swift also supports unstructured concurrency. [...] To create an unstructured task that runs on the current actor, call the Task.init(priority:operation:)
initializer. To create an unstructured task that’s not part of the current actor, known more specifically as a detached task, call the Task.detached(priority:operation:)
class method.
This is also documented in Task.init
(my bold)
Runs the given nonthrowing operation asynchronously as part of a new top-level task on behalf of the current actor.
You can also see this in action - a child task created by awaiting an async function is cancelled when its parent is cancelled, but a task created by Task.init
is not.
let task = Task {
let notChild = Task {
await Task.sleep(1_000_000_000)
if !Task.isCancelled {
print("Not Child!")
} else {
print("Child!")
}
}
}
task.cancel()
// prints "Not Child!"
func childTask() async {
await Task.sleep(1_000_000_000)
if !Task.isCancelled {
print("Not Child!")
} else {
print("Child!")
}
}
let task = Task {
await childTask()
}
task.cancel()
// prints "Child!"
Anyway, back to your question, so what actually is the difference between Task.init
and Task.detached
? The first quote in my answer addressed that a little bit, and this is also mentioned in the documentation of Task.init
too:
Like Task.detached(priority:operation:)
, this function creates a separate, top-level task. Unlike Task.detached(priority:operation:)
, the task created by Task.init(priority:operation:)
inherits the priority and actor context of the caller, so the operation is treated more like an asynchronous extension to the synchronous operation.
Essentially, it's about the actor on which the task is executed, so even if there is no current task, but there is a current actor, Task.init
and Task.detached
will still do different things. Task.init
will run the task on the current actor, whereas Task.detached
will run the task detached from any actor.
See also: https://www.hackingwithswift.com/quick-start/concurrency/whats-the-difference-between-a-task-and-a-detached-task