Demystifying Swift’s Task() for Beginners: An Introduction to Asynchronous Programming.(Part-2)
3 min readApr 13, 2024
In Part 1 you got introduced to task and its creation, in this part you are going to learn more about how different Task API
Task()
When a Task is created as below
Task {
//Do some async stuff
}
- This runs on a default task group or any current group that exists.
- These tasks inherit the default Actor isolation and maintain the current context.
- The lifecycle of the task is bound to the current task group, if any. When the task group completes, the task is also completed.
- The task can also create a detached Task for other purposes.
Task.detached()
Detached task can be created as below
Task.detached {
let info = try await fetchSomeInfo()
}
- The
Task.detached()
function creates a task that is detached from any task group. - The task is independent of the current task group and has its lifecycle.
- If the task is cancelled, it does not affect other tasks or the task group it was created in.
- Use
Task.detached()
when you want to create a standalone task that operates independently of the current task group.
Task.yield()
Task.yield() can be used when you have a Task that is bulk or not very important to run on a main thread.
This function is particularly useful in cooperative multitasking scenarios where you want to give other tasks a chance to run, especially in long-running or computationally intensive tasks, to prevent blocking the execution of other concurrent tasks.
import Foundation
// Define an asynchronous task that performs a long-running computation
func longRunningTask() async {
print("Starting long-running task...")
// Simulate a computationally intensive operation
for i in 1...10 {
print("Processing step \(i)...")
// Simulate yielding control to other tasks after each step
Task.yield()
}
print("Long-running task completed.")
}
withTaskGroup
- All the APIs referred to above are unstructured Tasks, but with withTaskGroup you can create structured Tasks.
- It allows you to spawn multiple child tasks, collect their results, and perform actions upon completion.
withTaskGroup
is useful when you need to perform parallel asynchronous operations and collect their results without handling errors.- The error can be handled with
withThrowingTaskGroup
import Foundation
// Define an asynchronous function that simulates fetching data from a remote server
func fetchDataFromServer(_ index: Int) async -> String {
// Simulate a network delay
await Task.sleep(1_000_000_000) // 1 second
// Simulate data retrieval
return "Data \(index) fetched from server"
}
// Define a function to initiate the task group
func initiateTaskGroup() {
Task {
// Create a task group
await withTaskGroup(of: String.self) { group in
// Spawn multiple child tasks within the group
for i in 1...3 {
group.add {
// Call the asynchronous function to fetch data
let result = await fetchDataFromServer(i)
return result
}
}
// Wait for all tasks in the group to complete and collect their results
for await result in group {
print("Received data:", result)
}
}
// All tasks in the group have completed
print("All tasks completed.")
}
}
// Call the function to initiate the task group
initiateTaskGroup()
- The
initiateTaskGroup
the function creates a task group usingwithTaskGroup
. Within the group, it spawns multiple child tasks using thegroup.add
method. Each child task asynchronously fetches data from the server using thefetchDataFromServer
function. - After adding all tasks to the group, we use a
for await
loop to wait for and collect the results of all tasks in the group.
The practical approach of the above code will be the next part(coming soon)