Correct way of dealing with throwing functions inside a Task

Hello everyone,

I am currently going through a course on async await and I think I'm lost on how to handle errors thrown inside a Task. It seems the general sentiment is that errors should be handled inside the context of the task. But what if that's not possible? E.g.

func myFunction() throws {
   Task {
       try await someFunctionThatMayFail
   }
}

If I then call myFunction as:

do {
    try myFunction()
} catch {
  print("Oh no, I got an error: \(error)")
}

even if there's an error thrown, it will never trigger the catch.

I have seen suggestions for extensions: swift - Throwing erros inside a root Task - Stack Overflow Is this a correct and viable approach?

There also seems to be a suggestion to use the result of the task.

My question is, given a scenario as described in the snippets, is there a correct way of handling this?

The result of Task { } when the body throws errors is a Task<ResultType, Error>. To wait for the task to complete, and handle any error it may have thrown, you should save the task value, and then try await task.value when you want to handle it.

If you want to await the task in the same scope that you create it, then structured concurrency provides some nice benefits. Instead of using Task { foo() } you can write async let result = foo(); … try await result. async let child tasks can’t outlive their scope, and automatically inherit priority and other attributes from their parent task.

4 Likes

Relevant discussion: Task initializer with throwing closure swallows error