SkillAgentSearch skills...

RXPromise

An Objective-C Class which implements the Promises/A+ specification.

Install / Use

/learn @couchdeveloper/RXPromise
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

RXPromise

A thread safe implementation of the Promises/A+ specification in Objective-C with extensions.

If you like a more modern "Skala-like" futures and promise library implemented in Swift, you may look at FutureLib.

Important Note:

For breaking changes and API extensions please read the CHANGELOG document.

A Brief Feature List:

  • Employs the asynchronous "non-blocking" style

  • Supports chained continuations

  • Supports cancellation

  • Simplifies error handling through error propagation

  • Thread-safe implementation

  • Handlers can execute on diverse execution contexts, namely dispatch_queue, NSThread, NSOperationQueue and NSManagedObjectContext.

  • RXPromise objects are lightweight.

Credits

RXPromise has been inspired by the Promises/A+ specification which defines an open standard for robust and interoperable implementations of promises in JavaScript.

Much of the credits go to their work and to those smart people they based their work on!

How to Install

For install instructions, please refer to: INSTALL

Contents

  1. Introduction
  2. What is a Promise
  3. Where Can We Use Promises
  4. A Non-Trivial Example
  5. Understanding Promises
  6. The Resolver Aspect
  7. The Promise Aspect
  8. Using a Promise at the Resolver Site
  9. The Asynchronous Task's Responsibility
  10. Creating a Promise
  11. Resolving a Promise
  12. Forwarding Cancellation
  13. Using a Promise at the Call-Site
  14. Defining a Continuation
  15. A Continuation Returns a Promise
  16. Chaining
  17. Branching
  18. The then, thenOn and thenOnMain Property
  19. The Execution Context
  20. Error Propagation
  21. Cancellation

Introduction

What Is A Promise

In general, a promise represents the eventual result of an asynchronous task, respectively the error reason when the task fails. Equal and similar concepts are also called future, deferred or delay (see also wiki article: Futures and promises).

The RXPromise implementation strives to meet the requirements specified in the Promises/A+ specification as close as possible. The specification was originally written for the JavaScript language but the architecture and the design can be implemented in virtually any language.

Asynchronous non-blocking

A RXPromise employs the asynchronous non-blocking style. That is, a call-site can invoke an asynchronous task which immediately returns a RXPromise object. For example, starting an asynchronous network request:

RXPromise* usersPromise = [self fetchUsers];

The asynchronous method fetchUsers returns immediately and its eventual result will be represented by the returned object, a promise.

Given a promise, a call-site can obtain the result respectively the error reason and define how to continue with the program when the result is available through "registering" a Continuation.

Basically, a "Continuation" is a completion handler and an error handler, which are blocks providing the result respectively the error as a parameter. Having a promise, one or more continuations can be setup any time.

Registering a continuation will be realized with one of three properties of RXPromise: then, thenOn or thenOnMain, and providing the definitions of the handler blocks:

usersPromise.then(^id(id result){
    NSLog(@"Users: %@", result);
    return nil;
},
^id(NSError* error){
    NSLog(@"Error: %@", error);
    return nil;
});

A more thorough explanation of the "Continuation" is given in chapter Understanding Promises and Using a Promise at the Call-Site.

Thread-Safety

RXPromise's principal methods are all asynchronous and thread-safe. That means, a particular implementation utilizing RXPromise will resemble a purely asynchronous and also thread-safe system, where no thread is ever blocked. (There are a few exceptions where certain miscellaneous methods do block).

Explicit Execution Context

The Execution Context defines where the continuation (more precisely, the completion handler or the error handler) will finally execute on. When setting up a continuation for a RXPromise we can explicitly specify the execution context using the thenOn and the thenOnMain property. The execution context is used to ensure concurrency requirements for shared resources which will be accessed concurrently in handlers and from elsewhere. The execution context can be a dispatch queue, a NSOperationQueue, a NSThread or even a NSManagedObjectContext.

See also The Execution Context.

Cancellation

Additionally, RXPromise supports cancellation, which is invaluable in virtual every real application. For more details about Cancellation please refer to chapter Cancellation.

Set of Helper Methods

The library also provides a couple of useful helper methods which makes it especially easy to manage a list or a group of asynchronous operations. Please refer to the source documentation.

Contents ^

Where Can We Use Promises?

Unquestionable, our coding style will become increasingly asynchronous. The Cocoa API already has a notable amount of asynchronous methods which provide completion handlers and also has numerous frameworks which support the asynchronous programming style through the delegate approach.

However, getting asynchronous problems right is hard, especially when the problems get more complex.

With Promises it becomes far more easy to solve asynchronous problems. It makes it straightforward to utilize frameworks and APIs that already employ the asynchronous style. A given implementation will look like it were synchronous, yet the solution remains completely asynchronous. The code also becomes concise and - thanks to Blocks - it greatly improves the locality of the whole asynchronous problem and thus the code becomes comprehensible and easy to follow for others as well.

A Non-trivial Example

Imagine, our objective is to implement the task described in the six steps below:

  1. Asynchronously perform a login for a web service.
  2. Then, if that succeeded, asynchronously fetch a list of objects as JSON.
  3. Then, if that succeeded, parse the JSON response in a background thread.
  4. Then, if that succeeded, create managed objects from the JSON and save them asynchronously to the persistent store, using a helper method saveWithChildContext: (see below).
  5. Then, if this succeeded, update the UI on the main thread.
  6. Catch any error from above steps.

Suppose, we have already implemented the following asynchronous methods:

In the View Controller:

/**
  Performs login on a web service. This may ask for user credentials
  in a separate UI.
  If the operation succeeds, fulfills the returned promise with @"OK",
  otherwise rejects it with the error reason (for example the user
  cancelled login, or the authentication failed on the server).
*/
- (RXPromise*) login;  

/**
  Perform a network request to obtain a JSON which contains "Objects".
  If the operation succeeds, fulfills the returned promise with a
  `NSData` object containing the JSON, otherwise rejects it with the
  error reason.
*/
- (RXPromise*) fetchObjects;

A real application would also use Core Data for managing a persistent store. Our "Core Data Stack" is quite standard having a "main context" executing on the main thread, whose parent is the "root context" running on a private queue with a backing store. Assuming this Core Data stack is already setup, we implement the following method:

/**
  Saves the chain of managed object contexts starting with the child
  context and ending with the root context which finally writes into
  the persistent store.
  If the operation succeeds, fulfills the returned promise with the
  childContext object, otherwise rejects it with the error reason.
*/
- (RXPromise*) saveWithChildContext:(NSManagedObjectContext*)childContext;

Having those methods, the following single statement asynchronously executes the complex task defined in the six steps above:

RXPromise* fetchAndSaveObjects =
[self login]
.then(^id(id result){
    return [self fetchObjects];
}, nil)
.then(^id(NSData* json){
    NSError* error;
    id jsonArray = [NSJSONSerialization JSONObjectWithData:json
                                                   options:0
                                                     error:&error];
    if (jsonArray) {
        NSAssert([jsonArray isKindOfClass:[NSArray class]]); // web service contract
        return jsonArray;  // parsing succeeded
    }
    else {
        return error;      // parsing failed
    }
}, nil)
.then(^id(NSArray* objects){
    // Parsing succeeded. Parameter objects is an array containing
    // NSDictionaries representing a type "object".

    // Create managed objects from the JSON and save them into
    // Core Data:
    NSManagedObjectContext* moc = [[NSManagedObjectContext alloc]
                      initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    moc
View on GitHub
GitHub Stars276
CategoryProduct
Updated3mo ago
Forks23

Languages

Objective-C++

Security Score

77/100

Audited on Dec 15, 2025

No findings