Bad practice: using an unsafe continuation
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 the problem with this code?
Here we’re making a legacy asynchronous function, that relies on a completionHandler
, compatible with the modern async
/ await
syntax.
And to achieve this, we’re using the function withUnsafeContinuation
.
But you might already know that there’s another function that seemingly performs the same job: a function called withCheckedContinuation
…
So what’s the difference between these two functions?
The difference lies around the continuation
.
The documentation states that the continuation
is an opaque representation of the state of our program and that it must be called exactly once.
And that’s where the difference lies: withCheckedContinuation
will add runtime checks to detect any improper use of the continuation and warn you if it were to happen.
But on the other hand, withUnsafeContinuation
will perform none of these checks.
So let’s see what happens if we make a mistake in how we use the continuation
!
If we forget to call the continuation
, then our program will be awaiting for ever. But when using a checked continuation, we get a warning in the console 👍
And if we call the continuation
more than once, the unsafe version will result in undefined behavior whereas the checked version will crash explicitly.
So if you go with the unsafe approach and misuse the continuation
, it will be much harder to notice it, especially in production 😔
But then why do we even have the option of using withUnsafeContinuation
?
It’s because these additional checks come with a small cost.
And in some performance heavy situations, it can make sense to disable them.
However, in most cases this shouldn’t be an issue and you’ll be much better using withCheckedContinuation
because of the extra safety it provides.
That’s all for this article: I hope it will help you avoid sneaky production bugs in the future!
Here’s the code if you want to experiment with it:
await withUnsafeContinuation { continuation in
legacyAsyncFunction { result in
continuation.resume(returning: result)
}
}
await withCheckedContinuation { continuation in
legacyAsyncFunction { result in
continuation.resume(returning: result)
}
}