Of the abstractions introduced in the Parallel Programming Library, the IFuture<T> seems to be the one that many people have difficulty understanding.

Both TTask and TParallel.For seem reasonably easy to understand conceptually, even if you don’t understand all the details. TTask is about some code you want to run in parallel to the main thread. TParallel.For is about performing some actions on the items in a collection in parallel. In both cases you are focussed on the actions you want to perform.

I’ve struck many people however, who struggle to conceptually understand a Future, and when I drill into why, in most cases it’s because they are thinking about Futures in the same way: as being about some code or actions they want to perform. I’ve even had someone say to me “This is about some job I want to have happen in the future, but not now? I don’t get it.”

If you’re in this situation, stop thinking about Futures in the context of the activities you want to do.

A Future is not about a task you want to perform in the future, it’s about a value you know you’ll want in the future.

Let’s say I’m trying to make a decision about whether I can process a customer order:

procedure TFormThreading.OrderItem(const CustomerID, ItemID: String; OrderQty: Integer); var StockLevel : Integer; PaidTheirBills : Boolean; begin StockLevel := GetStockLevel(ItemID); // very slow PaidTheirBills := HaveTheyPaid(CustomerID); // also very slow if (StockLevel >= OrderQty) and (PaidTheirBills) then // process the order end; 1 2 3 4 5 6 7 8 9 10 11 procedure TFormThreading . OrderItem ( const CustomerID , ItemID : String ; OrderQty : Integer ) ; var StockLevel : Integer ; PaidTheirBills : Boolean ; begin StockLevel : = GetStockLevel ( ItemID ) ; // very slow PaidTheirBills : = HaveTheyPaid ( CustomerID ) ; // also very slow if ( StockLevel > = OrderQty ) and ( PaidTheirBills ) then // process the order end ;

In this scenario, there might be several pieces of information I need before I can go ahead and process the order, such as whether we have enough stock, whether the customer has good enough credit, and potentially others.

Further, let’s assume that the calls to find out our stock levels and also the customer’s credit worthiness are time consuming calls. I could do them sequentially, as above, but then every order will take a long time.

Yes, I could rejig the logic and check the StockLevel straight after retrieving it, exiting if we don’t have enough, but that will still leave me with a slow process in the cases where we do.

But if we step back, we know we need two values to make the decision: StockLevel and PaidTheirBills. Further, the decision will be made in the future, we first have to retrieve the values. So, let’s use a Future.

Let’s rejig the code to look like this:

procedure TFormThreading.OrderItem(const CustomerID, ItemID: String; OrderQty: Integer); var StockLevel : IFuture<Integer>; PaidTheirBills : Boolean; begin StockLevel := TTask.Future(function : Integer begin // very slow Result := GetStockLevel(ItemID); end); PaidTheirBills := HaveTheyPaid(CustomerID); // also very slow if (StockLevel.Value >= OrderQty) and (PaidTheirBills) then // process the order end; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 procedure TFormThreading . OrderItem ( const CustomerID , ItemID : String ; OrderQty : Integer ) ; var StockLevel : IFuture <Integer> ; PaidTheirBills : Boolean ; begin StockLevel : = TTask . Future ( function : Integer begin // very slow Result : = GetStockLevel ( ItemID ) ; end ) ; PaidTheirBills : = HaveTheyPaid ( CustomerID ) ; // also very slow if ( StockLevel . Value > = OrderQty ) and ( PaidTheirBills ) then // process the order end ;

Instead of being an Integer, StockLevel is now an IFuture<Integer>. It’s not an Integer we need right now, but one we need in the future. We instantiate it by calling TTask.Future, passing in an anonymous method which contains the code needed to calculate its value. This will start running in the background, but in the meantime, we’ll continue on to calculate the other value we need, PaidTheirBills, another time consuming call.

When it comes time to make our decision, a couple of things might have happened.

Our anonymous method might have already finished running, in which case calling StockLevel.Value will immediately return the result, which we can then compare to OrderQty.

Our anonymous method might still be running, in which case the call to StockLevel.Value will block until it finishes.

But either way, we know that both values will be calculated and compared before we process the order, but it’s likely the user will not have waited as long for the result.