C++ – Use cases and usage patterns of futures vs callbacks

asyncasynchronous-programmingcjavascript

I've become interested in futures lately, mostly due to their inclusion in standard C++ – but I see there is an active debate over futures vs. callbacks in many programming areas, such as Node.js.

I'd like to ask is if there are any well-known patterns/use-cases where futures provide a clear advantage (in terms of readability and ease of implementation) to callbacks?

We all know the main downsides associated with callbacks: indirection in terms of reading the code, and the potentially deeply nested and confusing lambdas in languages like Javascript.

The advantage of using futures is they eliminate the code indirection and let you write code in a "synchronous" style, but provide you with a handler that lets you check on the status of some background operation. In that sense, I don't think it would be too subjective/controversial to claim that futures are a better syntactic fit with how the human mind tends to think (do this, do that, do this while waiting for that, etc).

However, when I actually use futures in practice, I often run into the following issue:

A typical use case for futures is where you manage many async operations via an array of future objects, like a vector<future> futures (using C++ syntax). I think a key insight is that futures require that you actively check each async operation to see if it's done, whereas with callbacks you passively wait for each event to complete. But since, in the general case, you have no way of knowing which event will complete first, you may lose out on some parallelism if you're iterating over an array of futures and future[10] finishes 5 seconds before future[0], but you're sitting there waiting for future[0] to complete.

We can mitigate this with a "wait_for" type of operation – instead of waiting on each future, we can wait for a certain short interval, and then proceed to continue iterating. But this is basically like busy waiting, consuming extra CPU resources. With callbacks, you wouldn't need to do any of this because callback code is invoked passively by some background event reactor. Basically, by iterating over an array of futures in application code, you're sort of "re-implementing" a poll event reactor at a higher level.

One straightforward use case for futures I've found is any situation where you have one or more async calls, and you can interleave some CPU cycles between calls. Suppose you have to call an HTTP web service twice, but the result of the first call gives you the opportunity to start processing immediately without the need to wait for the second call to complete. Futures make this easy, because you can just say:

future result1 = some_remote_call();
object data = result1.get(); // wait for result1 to complete

future result2 = another_remote_call();

// ... do some processing here while we're still waiting on result2 ... //

So that's definitely a nice use case, but the more common case of waiting for N events to occur seems better suited for callbacks, despite the code-indirection we suffer using callbacks.

So, what are some well-known usage patterns/use-cases where futures provide a clear advantage (in terms of code readability and ease of implementation) over callbacks?

Best Answer

What you need to understand is that the C++ standard library quite possibly has the worst implementation of futures conceivable by man. What C++ does is little more than a weak wrapper around a semaphore. Most future implementations encourage use of callback lambdas extensively, to chain several futures together asynchronously using then, and allow you to easily create a single future that is based on a collection of other futures. They have none of the issues you are raising here.

Look at Q for one of the best implementations, to see how futures were really designed to be used. If you search around, there are people bringing those ideas into C++. It's not terribly difficult to implement yourself.