Asynchronous Programming with PromiseKit
Asynchronous Programming
An asynchronous model allows multiple things to happen at the same time. When you start an action, your program continues to run. When the action finishes, the program is informed and gets access to the result (for example, the data read from disk)
CallBacks
Callbacks is one of the way to achieve asynchronicity. In this approach we pass an extra argument to the function, a callback function (executable function). The action is started, and when it finishes, the callback function is called with the result.
As shown in Figure 1 since getDataFromServer()
will take time we don’t want to wait so we create a callback function and pass in an argument and when the data is available getDataFromServer()
will execute that function.
Problem With CallBacks
There is no problem with the callbacks :p until you have only one . As shown in Figure 2 we have a dependent nested async task which we perform using callbacks and you will find two problems
- Callbacks pyramid of doom
- Control flow hell
Promises
As shown in Figure 3 we solved the problems using Promises. Improvements are listed below
- Simplify the async code
- Avoided nested callbacks with the use of chain (We make async code to read like sync code)
- Removed pyramid of brackets
- Separate error handling logic to one place in catch block
- Removed duplicate code of error handling
What is Promise
A promise is an object with a “.then()” method that represents an operation whose result may be unknown. Whoever has access to the object can use the “.then()” method to add callbacks to be notified about the successful completion or failure of the operation.
Benefits of Promise
- Simplify your async code
- Increased readability
Key Difference Between Callbacks and Promises
A key difference between the two is when using the callback approach, we’d normally just pass a callback into a function that would then get called upon completion in order to get the result of something. In promises, however, you attach callbacks on the returned promise object.
Main rules for a compliant Promise/A+ implementation are:
Rule 1 → A promise or “thenable” is an object that supplies a standard compliant then
function.
Rule 2→ A pending promise may transition into a fulfilled or rejected state
Rule 3 → A fulfilled or rejected is settled, and must not transition into any other state
Rule 4 → Once a promise is settled, it must have a value. This value must not change
Open Source Libraries
PromiseKit , Hydra, Promises (Maintain by Google)
PromiseKit 6.8
PromiseKit is an implementation of promises in swift. It offers extensions to Apple’s APIs as well and supports large amount of functional components
Getting Started
then → Take value from the previous promise and return new Promise
done → Take value from the previous promise and doesn’t return anything. It is typically the end of the “success” part of the chain
catch → Promise can represent represent success or failure value in future. Any fail in the chain will pass to the catch block
As shown in Figure 4 we have a sequence of asynchronous tasks to be done one after another and we do it using promise chaining. Followings tasks are happening in the below code
getUserGitHubProfile()
is an async task that immediately return Promise object as shown in Figure 5.- Initially promise object is unresolved having state
pending
(unresolved means it make promise to you that in future when task finish it will notify you) - Promise object also have
then
method called operator as stated in Rule 1. WhengetUserGitHubProfile() (
async task) successfully complete its task, it will notify throughthen
block and in case of failure it will notify throughcatch
block (call chain fails). Rememberthen
block also return promise object to continue a promise chain - As shown in Figure 5 we fetch user data from server and when we successfully get response we call
fulfill
method and it will call the nextthen
method in the chain with the provided data throughfulfill
caller. Now state settled withfulfilled
and will never resettled as stated in Rule 4 - As we got user data in
Data
format inthen
block and we called async task as shown in Figure 6 to decode into the Github model to get the userimageUrl
and pass it to the next async task. When this task finish it will notify to the next then block with theimageUrl
String . - As shown in Figure 7 when we get imageUrl from the previous async task we fetch image from server and pass image to the next async task which is done block since done will not return any Promise object and It is typically the end of the “success” part of the chain as shown in Figure 4
- Any error in the chain will pass through catch block and and will not execute the subsequent
then
block
As shown in Figure 5 getUserGitHubProfile()
will return Promise object. The seal
object that the Promise
initializer provides to you defines many methods for handling garden-variety completion handlers. If your promise is successful you call fulfilled to notify next then or done block and if it fails you call reject to break the promise chain and execute the catch block
As shown in Figure 6 we didn’t use do/catch block when decoding because PromiseKit will handle it. If the decoding fails it will call catch
block of the promise chain
ensure
No matter the outcome of your chain — -failure or success — -your ensure
handler is always called.
As shown in Figure 8 if the chain is successful means done block runs without any error or chain is failed means catch block is called ensure block will always execute. It’s like final block in do/catch
Should I use [weak self] in PromiseKit blocks?
Parallel/Concurrent Async Task Execution
Dispatch Group
As shown in Figure using Dispatch Group we make a concurrent batch async execution and when all done we notify user with all the responses object.
Promises
As shown in Figure 10 we implemented async batch requests using promiseKit.
when → when
takes promises
, waits for them to resolve and returns a promise containing the results. As with any promise chain, if any of the component promises fail, the chain calls the next catch
. As shown in Figure 10 we are saying when all these call finishes successfully execute done method with all the responses and if any of the call fails catch
block will execute.
As shown in Figure 11 implementation details of getToDoDataFromServer()
which is using sequential Promise chain and when resolved return data to previous promise chain and change the status of this async task to Fulfilled if success. We used nested Promise chain and error in this chain will fail this chain and parent chain as well and catch block of the parent chain will execute
Map, CompactMap in PromiseKit
Before PromiseKit 4, then
handle all case Now then split it into then
, map
and done
method /operator
Map
→ Provides you the result of previous Promise and return object / value. Map doesn’t return any promise
CompactMap
→ Provides you the result of previous Promise and return optional object / value. Compact Map also doesn’t return any promise.
As shown in Figure 12 we perform a number of tasks
- First we call
getDataFromServer
to fetch Todo data from server which return data in bytes (Data) format - We pass bytes to
map
operator which transform data into ToDo model using decode function and return decoded model object to next chain block which iscompactMap
CompactMap
block get the decoded object and return title property which is optional as shown in Figure 13 to the done block- Done block get the unwrap value of the title in it’s parameter
Threading
Promise chain works on main thread as shown in Figure 14 firstly
, then
, done
and catch
block will run on main
thread . Async task getDataFromServer
will also run on main thread if we don’t explicitly change the thread using Alamofire.request
method
We can change the thread of the chain as shown in figure 15 decodeGithhubProfile
method will run on background
thread
TroubleShoot Guide :
99% of compilation issues involving PromiseKit can be addressed or diagnosed by one of the fixes below in the link
Useful Links
https://github.com/mxcl/PromiseKit/blob/master/Documentation/GettingStarted.md