Promises: basics

In my previous article, I introduced the concept of promise, a technique that allows us to write async code in a more sync-ish manner. In this article and the following ones I will try to dive into the definition and expectancies of a promise.

So what is a promise? A promise represents the eventual result of an asynchronous operation. This means that you can use a promise wherever you use an async operation, and the promise will send the result of that operation (success or failure) to you.

Please note that the promise does not correspond to the async operation itself, but to the result of the operation. Thus, promises don’t offer support for cancelling, retrying, or another features of async operations. Promises are here to deliver the operation result to you.

A promise can only be in one of the following three states: pending, resolved, or rejected. A newly created promise is in the pending state, from which it can move to the resolved state by calling its resolve method, or it can move to the rejected state by calling its reject method.

Their usage is also simple: once you get your hands a promise, you can add success or failure callbacks on it. Adding callbacks can be done by calling the then method, method that accepts two parameters: a callback for success, and a callback for failure. Any of them can be nil, telling the promise that we don’t care about that certain resolution of the async operation.

// authenticateWithEmail:password: returns a promise
[authenticator authenticateWithEmail:email password:password].then(^(MYUser *user){
    // I got authenticated, yay
}, ^(NSError *error){
    // Something bad happened
});

The success callbacks are called when the promise gets resolved, while the failure callbacks are called when the promise is rejected. The promise guarantees that: 1) the callbacks will be executed in the order they were added, and 2) the callbacks will be executed after the current scope ends.

#2 allows developers to make  some extra configuration after the promise is dispatched. Below you can find a short example.

//retrieveBookWithId: returns a promise
[booksManager retrieveBookWithId:@"1234"].then(^{
    self.fetching = NO;
}, nil);
self.fetching = YES;

Even if the retrieveBookWithId: promise gets instantly resolved (e.g. if we return from a cache), we can be sure that the fetching flag will be set in the order we expect to.

At the very basic level you can use promises when you need support for multiple operation completion callbacks. But that’s not all a promise can do, executing callbacks based on the operation result. Thanks to the very well designed specifications of Promises/A+, you can do much, much more. One of the real power of promises comes from the fact that they can be chained in such a way that: 1) you write code in a sync-ish manner by naturally writing the promises one below the previous one, and 2) failed promises that should break the chain will break the chain. More on this in a future article.

To summarise the discussion so far, if you want to add promise support to your async operation, you need to create a promise and resolve or reject it when the operation finishes execution. If you want to use a promise, you need to add resolve/reject callbacks to that promise.

Stay tuned for continued discussions on promises. Meanwhile, if you have any questions you can leave them as comments.

Update: You can check my next article on promises here: http://blog.cristik.com/2015/03/promises-advanced/

Leave a comment

Your email address will not be published. Required fields are marked *