This article discusses the use of promises in transforming asynchronous code into synchronous code, making it more readable and maintainable.
Abstract
The article introduces the concept of promises, a tool used to write asynchronous code in a more readable and maintainable way. It explains the problems with plain asynchronous code, such as callback hell and difficulty in understanding the code flow. The article then introduces promises and their benefits, including the ability to write asynchronous code as if it were synchronous. The author also mentions libraries that implement the async-await pattern in Swift, as it is not built-in. Finally, the article provides examples of how to use promises and promisification, the process of transforming a function with a callback into a function that uses a promise.
Opinions
The author believes that promises are a solution to the problems of asynchronous code, including callback hell and difficulty in understanding the code flow.
The author emphasizes the benefits of promises, such as making code more readable and maintainable.
The author suggests using libraries that implement the async-await pattern in Swift, as it is not built-in.
The author provides examples of how to use promises and promisification, which they believe is a powerful pattern.
The author encourages readers to study promises deeply to understand the wonderful things they can achieve with them.
The author mentions that promises come with their own grammar to concatenate operations and execute some in parallel instead of one after the other.
The author hints at a future article where they will share patterns of usage to interact with iOS API, transforming them into promises.
Concurrent Programming
Promisification
How to use promises to transform asynchronous code in synchronous code
All the apps need to execute asynchronous code. It is a piece of code that will be executed in a secondary thread or in another moment in time and it will return some results in a callback. Plain asynchronous code has some drawbacks in readability and code organization that can make really hard to work with them.
Promises are a tool that helps us in writing asynchronous code in a way that is easier to read and to maintain. In this article, I’d like to explain better the problem, what a promise is, and how to use it. In a future article, I’d like to share some pattern of usage to interact with iOS API, transforming them in promises
The Problems of Asynchronous Code
As we interact with different libraries and iOS frameworks, and even when we write our own code, it often happens that we have to call a function that asks for a callback as a parameter. The callback will then be invoked when the operation ends, providing a success or a failure.
This programming paradigm has been used a lot, but it has some drawbacks: the worst one is that it makes it really hard to read the code. In fact, when many asynchronous functions need to be invoked, it will result in the infamous callback hell.
As you can see, we have to perform 5 operations where one depends on the result of the previous. The resulting code has this arrow shape that we learned to despise: it’s hard to understand when a callback ends, especially in more real-life cases.
A solution to this problem could be to split the internal invocation into other functions that do not require a callback because they know what to do after. This will work in some simple cases, and it removes the arrow shape of the code, but we are still inside our hell made of callbacks.
This is the code when we split the callbacks into different functions. You can see that we don’t have the arrow shape anymore. However, we need to jump from a function to another to understand what’s going on. Imagine having these functions in different files and then say hello to your mental sanity: having to jump back and forth to different files to understand the flow of a piece of code!
Promises, to the Rescue!
What if I told you that there is a way to write the code above as if it was synchronous? How good will it be if the code was like the following?
Wouldn’t it be awesome?
What is a promise?
The way to achieve this is through Promises. A Promise is an object that encapsulates a unit of work and, as soon as the work is done, it will contain the result of that work. In our case, we have a promise that contains the result of the download of the user, another with the favorite movies, and so on.
Of course, given that these operations are asynchronous and we cannot block the UI of our app while we wait for the results: we need a construct that allows us to wait for the execution to complete before moving to the next step. This instruction is called await(promise:) and it’s sole purpose is to pause the current thread until the promise passed as argument finish its execution.
How to use them.
Many languages today supports the async-await pattern natively (Javascript, Python, C#, F#, Rust, Kotlin, even C++). Unfortunately for us, Swift does not have this mechanism built-in. However, there are several libraries that implement this pattern: HydraAsync, PromiseKit, Google/Promises, SwiftTask, …
The way they work is more or less always the same. You create a promise, which will return a specific type, and implement the asynchronous code in the body of the promise. When your code finishes successfully, you resolve the promise; otherwise, you reject it with an error. The Promise object and the await function will take care of the machinery of waiting for the asynchronous code, bring back the context to the original thread, and return the value.
Let’s see how to rewrite the code using the promises. In the example, I will use the syntax provided by Hydra, because it is the library that I know better.
The process of transforming a function with a callback into a function that uses a promise is called Promisification. The name comes from one of the first function that does this, from Node.js.
As you can see, it requires a bit of work, but the final result is awesome: a person looking at the code will immediately understand what is going on, without having to move back and forth between different files or she doesn’t have to understand when a closure starts and when it ends.
Final Thoughts
This pattern is very powerful. We just scratch the surface of it and we already saw how powerful it is in clarity and readability.
Promises bring much more power: they usually come with their own grammar to concatenate operations and to execute some of them in parallel instead of one after the other. I strongly suggest choosing one of the libraries above and studying it deeply, to understand which wonderful things you can achieve with them.
If instead, you prefer a more story-telling approach, stay tuned! Next week I’d like to talk about how we used promises to interact with iOS API: presenting an alert or a modal and waiting for the user input, working with the permissions dialog, and also how to use promises to simplify the delegate pattern!