By default, all Swift functions are synchronous, but what does that mean?
A synchronous function is one that executes all its work in a simple, straight line on a single thread. Although the function itself can interact with other threads – it can request that work happens elsewhere, for example – the function itself always runs on a single thread.
This has a number of advantages, not least that synchronous functions are very easy to think about: when you call function A, it will carry on working until all its work is done, then return a value. If while working, function A calls function B, and perhaps functions C, D, and E as well, it doesn’t matter – they all will execute on the same thread, and run one by one until the work completes.
Internally this is handled as a function stack: whenever one function calls another, the system creates what’s called a stack frame to store all the data required for that new function – that’s things like its local variables, for example. That new stack frame gets pushed on top of the previous one, like a stack of Lego bricks, and if that function calls a third function then another stack frame is created and added above the others. Eventually the functions finish, and their stack frame is removed and destroyed in a process we call popping, and control goes back to whichever function the code was called from.
Synchronous functions have an important downside, which is that they are blocking. If function A calls function B and needs to know what its return value is, then function A must wait for function B to finish before it can continue. This sounds self-evident, I know, but blocking code is problematic because now you’ve blocked a whole thread – it might be sitting around for a second or more waiting for some data to return.
You’re probably thinking that waiting for a second is nothing at all, but in computing terms that’s an ice age! Keep in mind that the Neural Engine in Apple’s A14 Bionic CPU – just one part of the chip that powers the iPhone 12 – is capable of doing 11 trillion things per second, so if you block a thread for even a second that’s 11,000,000,000,000 things you could have done but didn’t.
One solution is to say “Well, if we’ve got a thread that’s currently blocked, we should just launch a new thread.” That’s definitely a solution, but it’s also a path towards thread explosion and we’ve already covered why that’s every bit as bad as it sounds.
So, although synchronous functions are easy to think about and work with, they aren’t very efficient for certain kinds of tasks. To make our code more flexible and more efficient, it’s possible to create asynchronous functions instead.
SPONSORED Join a FREE crash course for iOS devs who want to become complete senior developers — from October 18th to 24th. Learn how to apply iOS app architecture patterns through a series of lectures and practical coding sessions.
Link copied to your pasteboard.