Understanding Async Programming with Starbucks
The Pumpkin Spice Latte season is here! So I thought I'd introduce the concepts of Async programming with a little help from the world's largest Coffee Store
I've worked and trained with many developers over the years, and I found that many people struggle to get their head around asynchronous programming. Asynchronous structures like Futures, Promises and Blocks are literally everywhere now. Yet, they're still difficult to understand, so I thought I can help you wrap your head around it with some help from Starbucks. That said, grab your Pumpkin Spice Latte and get ready for a Caffeine-fueled adventure through the world of Async programming ☺
Pre-Starbucks - The Synchronous Model
Let's take a moment to picture yourself in line at your favorite coffee store, where they can process only one transaction at the time. You get to the counter, what happens next?
- You put in an order for a quadruple espresso (it's going to be a long day ahead)
- You pay the extortionate price (when did coffee get so expensive?!)
- You stand still...
- While you stand your server goes and crushes some beans;
- You stand still...
- The barista turns some handle's, bangs a big metal thing loudly, steams, froth;
- You stand still...
- Everyone behind you stands still...
- The barista holds the little paper cup under the coffee machine as it pours your drink in;
- Everyone behind you waits, checking their phones/watches, wondering who the awkward person ordering a quadruple espresso is;
- Finally, the barista gives you your coffee;
- You grab your drink; add any additions of your choice and leave;
- Now the Server moves on to the next person, who orders a single bottle of water they've been holding the entire time, they pay and leave immediately!
Welcome to the world of synchronous programming! You ask a function to do something and you wait until they've done it. And if someone else wants to do something that could be much quicker, they have to wait!
Let's translate our coffee examples to a real world coding example:
- You call a method
getAllProducts
to download all available products to display in a table; - You wait while the method does its thing…
- While you wait, the method makes an HTTP request to a server to retrieve the product list;
- You wait…
- The server on the far side of the world runs a SQL query against a database;
- You wait…
- In fact, your whole app is waiting. The user is waiting. You called this method on the UI thread, meaning that any work to update the UI or handle touches/clicks is also waiting!
- The server sends the product list back over HTTP to your method;
- The user is still waiting, tapping frantically on the screen wondering why the hell the app isn't working!
- Finally, the method you called returns the product list to you;
- You render the products;
- Now the main thread is free, all of a sudden every single tap/click the user made is processed in one big go and all hell breaks lose as 100s of animations across the screen are all triggered at the same time!
Say 2hat!?
If this doesn't make sense, then take a look at the following code:
-(NSArray *) getProducts() {
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:@"http://coffeeshop.com/products"]];
[request setHTTPMethod:@"GET"];
NSURLResponse* response;
NSError* error = nil;
NSData* result = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response error:&error];
NSArray *array = [NSKeyedUnarchiver result];
return array;
}
This is a common code I've seen a lot of iOS developers write, frequently called from methods like:
- (void)viewDidLoad {
[super viewDidLoad];
self.products = [self getProducts];
}
Why is this bad? Well, it blocks the view from loading until all the products have been downloaded. On a bad 3G or 2G connection, this could take a very long time, leaving your users waiting impatiently! So let's see the alternative...
The Starbucks Solution!
Now those of you regulars to Starbucks know that what I described for ordering a coffee is not really what happens in Starbucks. Here's a more realistic Starbucks experience:
- You put in an order for a quadruple espresso (it's going to be a long day ahead)
- You pay the extortionate price (when did coffee get so expensive?!)
- You walk away and play on your mobile
- The next person in the line with the water pays for their water and leaves the store. As he is walking to the exit right by you while you are still waiting, you can hear that person mutter "Thank heavens Starbucks understands async!"
- In fact, you watch a number of people being served quickly!
- Someone then shouts "Matt! Your quadruple espresso is ready". You grab it and off you go!
So what's different? Well, you've given the barista a way to call you back when your drink is ready, so you can spend time doing other things, and anyone else waiting to put in an order can be served much faster. Even better, drinks that are quick to make might be handed out before yours, but you know you'll get a shout when yours is ready.
This is the idea behind async structures like Blocks, Promises and Futures. They are all a way for you to register an interest in a future event without having to stand there and wait. Let's see our Objective C example updated to use Blocks:
-(void) getProducts:(void (^)(NSArray *))completionHandler {
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:@"http://coffeeshop.com/products"]];
[request setHTTPMethod:@"GET"];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// This code will only be called when the HTTP request comes back
NSArray *array = [NSKeyedUnarchiver result];
completionHandler(array);
}];
}
The difference here is just like with Starbucks- you don't get your answer returned straight away (see the return type is void). Instead, you give the method a completionHandler
, a block which will be called when the result is ready for you. In the same way, you give the Barista your name to call you once your drink is ready!
Let's see how this changes our viewDidLoad
:
- (void)viewDidLoad {
[super viewDidLoad];
[self getProducts:^(NSArray *products) {
self.products = products;
// tell our UITableView that new data
// is available and it should reload
[self.tableView reloadData];
}];
}
So here we give our Block and when the products are available we handle it asynchronously inside of the block. In our case, we'll assume you have a UITableView
that you want to reloadData
when the products are downloaded. This means that now you can present the view to the user quickly, with a loading spinner and other buttons still being interactive. And when the products are available, the table view will load- voila!
Thank You Starbucks
![](https://www.filepicker.io/api/file/dCPvkqYEQYSuDtiV1C5w=> width=50% "")