The "serialization" of asynchronous tasks will actually be called the "continuation" (see also this wiki Continuation article.
Suppose your tasks can be defined as an asynchronous function / method with a completion handler whose parameter is the end result of the asynchronous task, for example:
typedef void(^completion_handler_t)(id result); -(void) webRequestWithCompletion:(completion_handler_t)completionHandler; -(void) showAlertViewWithResult:(id)result completion:(completion_handler_t)completionHandler;
Having blocks , the "continuation" can be easily completed by calling the following asynchronous task from the previous task completion block:
- (void) foo { [self webRequestWithCompletion:^(id result) { [self showAlertViewWithResult:result completion:^(id userAnswer) { NSLog(@"User answered with: %@", userAnswer); } } }
Note that the foo method gets "infected" asynchronously ;;)
That is, here the possible effect of the foo method, namely, printing the user's response to the console, is actually again asynchronous.
However, the "chain" of several asynchronous tasks, that is, the "continuation" of several asynchronous tasks, can become quickly cumbersome:
Implementing a “continue” with completion blocks will increase the indentation for each task completion handler. In addition, by implementing a tool that allows the user to cancel tasks in any state, as well as implement code to handle error conditions, the code becomes quite confusing, difficult to write, and difficult to understand.
The best approach to implementing continuation, as well as canceling and processing errors, uses the concept of Futures or Promises . A future or promise is the end result of an asynchronous task. Basically, this is just a different approach to the “signal of the end result” to the call site.
In Objective-C, "Promise" can be implemented as a regular class. There are third-party libraries that implement the "promise." The following code uses a specific implementation, RXPromise.
When using such a promise, you define your tasks as follows:
-(Promise*) webRequestWithCompletion; -(Promise*) showAlertViewWithResult:(id)result;
Note. The completion handler does not exist.
With the promise, the result of the asynchronous task will be obtained using the success or error handler, which will be registered using the then property of the promise. Either success or an error handler is called by the task at its completion: when it succeeds, the success handler will be called by passing its result to the result of the success handler parameter. Otherwise, when the task fails, it passes the reason to the error handler - usually an NSError object.
The main use of Promise is as follows:
Promise* promise = [self asyncTasks]; // register handler blocks with "then": Promise* handlerPromise = promise.then( <success handler block>, <error handler block> );
The success handler block has the result of a parameter of type id . The error handler block has an argument of type NSError .
Note that the promise.then(...) operator returns a promise that represents the result of any handler that is called when the “parent” promise was resolved either with success or with an error. The return value of the handler can be either an "immediate result" (by some object) or an "end result" represented as a Promise object.
A commented example of an OP problem is shown in the following code snippet (including complex error handling):
- (void) foo { [self webRequestWithCompletion] // returns a "Promise" object which has a property "then" // when the task finished, then: .then(^id(id result) { // on succeess: // param "result" is the result of method "webRequestWithCompletion" return [self showAlertViewWithResult:result]; // note: returns a promise }, nil /*error handler not defined, fall through to the next defined error handler */ ) // when either of the previous handler finished, then: .then(^id(id userAnswer) { NSLog(@"User answered with: %@", userAnswer); return nil; // handler result not used, thus nil. }, nil) // when either of the previous handler finished, then: .then(nil /*success handler not defined*/, ^id(NEError* error) { // on error // Error handler. Last error handler catches all errors. // That is, either a web request error or perhaps the user cancelled (which results in rejecting the promise with a "User Cancelled" error) return nil; // result of this error handler not used anywhere. }); }
The code, of course, requires more explanation. For a detailed and more detailed description and how cancellation can be achieved at any given time, you can look at the RXPromise library - an Objective-C class that implements the "Promise". Disclosure: I am the author of the RXPromise library.