Asynchronous Programming with PromiseKit

Ali Akhtar
8 min readApr 23, 2019

--

CallBack Hell

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.

Figure 1

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

  1. Callbacks pyramid of doom
  2. Control flow hell
Figure 2

Promises

As shown in Figure 3 we solved the problems using Promises. Improvements are listed below

  1. Simplify the async code
  2. Avoided nested callbacks with the use of chain (We make async code to read like sync code)
  3. Removed pyramid of brackets
  4. Separate error handling logic to one place in catch block
  5. Removed duplicate code of error handling
Figure 3

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

  1. Simplify your async code
  2. 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

  1. getUserGitHubProfile() is an async task that immediately return Promise object as shown in Figure 5.
  2. 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)
  3. Promise object also have then method called operator as stated in Rule 1. When getUserGitHubProfile() (async task) successfully complete its task, it will notify through then block and in case of failure it will notify through catch block (call chain fails). Remember then block also return promise object to continue a promise chain
  4. 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 next then method in the chain with the provided data through fulfill caller. Now state settled with fulfilled and will never resettled as stated in Rule 4
  5. As we got user data in Data format in then block and we called async task as shown in Figure 6 to decode into the Github model to get the user imageUrl and pass it to the next async task. When this task finish it will notify to the next then block with the imageUrl String .
  6. 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
  7. Any error in the chain will pass through catch block and and will not execute the subsequent then block
Figure 4

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

Figure 5

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

Figure 6
Figure 7

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

Figure 8

Should I use [weak self] in PromiseKit blocks?

That documentation is merely saying that you don’t have to worry about PromiseKit introducing “strong reference cycles” (previously known as “retain cycles”) because when the promise is fulfilled and the block finishes running, those strong references are automatically resolved for you. The choice of strong vs weak references is solely up to you

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.

Figure 9

Promises

As shown in Figure 10 we implemented async batch requests using promiseKit.

whenwhen 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.

Figure 10

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

Figure 11

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

  1. First we call getDataFromServer to fetch Todo data from server which return data in bytes (Data) format
  2. 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 is compactMap
  3. CompactMap block get the decoded object and return title property which is optional as shown in Figure 13 to the done block
  4. Done block get the unwrap value of the title in it’s parameter
Figure 12
Figure 13

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

Figure 14

We can change the thread of the chain as shown in figure 15 decodeGithhubProfile method will run on background thread

Figure 15

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

--

--

Ali Akhtar
Ali Akhtar

Written by Ali Akhtar

Senior iOS Engineer | HungerStation | Delivery Hero

No responses yet