Be careful wrapping a throwing function in a Task
You’re more of a video kind of person? I’ve got you covered! Here’s a video with the same content than this article 🍿
Can you guess what’s wrong with this code?
We have an asynchronous throwing function and for the purpose of this example it will always throw an Error
:
And then we have a synchronous throwing function that calls this asynchronous function through the use of a Task
:
At first glance, this code looks perfectly fine.
However, it has one very tricky pitfall!
Let’s say that we call the function loginUser()
inside a do
block, in order to catch
and handle the Error
:
If we run this code, we’ll notice that, even though the Error
is thrown, the code in the catch
block never gets executed.
This might seem strange but this is actually a totally normal and intended behavior!
What’s happening is that the Error
is indeed thrown, but it is caught by the Task
.
And from that point, the Task
has no way of re-throwing the Error
to the function loginUser()
, because the Task
is running asynchronously and the function loginUser()
has already finished executing!
And actually the reason why this confusion was made possible is because I’ve made a mistake when writing this code!
You see, I’ve declared the function loginUser()
as throwing, but there’s actually no call to a throwing function inside its scope.
Using the keyword throws
here is completely superfluous, and if I remove it we can see that the code still builds, but now we get a very clear warning that the catch
block will never be executed:
I’ve myself seen this mistake find its way in a codebase, so I would definitely recommend that you check that you also haven’t made it yourself!
Here’s the code if you want to experiment with it:
import Foundation
enum AuthenticationError: Error {
case invalidCredentials
}
func verifyUserCredentials() async throws {
throw AuthenticationError.invalidCredentials
}
func loginUser() {
Task {
try await verifyUserCredentials()
}
}
do {
try loginUser()
} catch {
print("Authentication failed: \(error)")
}