Demystifying Swift’s Task() for Beginners: An Introduction to Asynchronous Programming.(Part-2)

Vishwas Ng
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 using withTaskGroup. Within the group, it spawns multiple child tasks using the group.add method. Each child task asynchronously fetches data from the server using the fetchDataFromServer 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)

--

--