Page MenuHomePhabricator

Using Futures
libphutil Technical Documentation (Futures)

Overview of how futures work in libphutil.

Overview

Futures (also called "Promises") are objects which represent the result of some pending computation (like executing a command or making a request to another server), but don't actually hold that result until the computation finishes. They are used to simplify parallel programming, since you can pass the future around as a representation for the real result while the real result is being computed in the background. When the object is asked to return the actual result, it blocks until the result is available.

libphutil provides a number of future-based APIs, as they strike a good balance between ease of use and power for many of the domains where PHP is a reasonable language choice.

Each type of future is used to do a different type of computation (for instance, ExecFuture executes system commands while HTTPFuture executes HTTP requests), but all of them behave in a basically similar way and can be manipulated with the same top-level constructs.

Basics

You create a future by instantiating the relevant class and ask it to return the result by calling resolve():

$gzip_future = new ExecFuture('gzip %s', $some_file);
$gzip_future->start();

// The future is now executing in the background, and you can continue
// doing computation in this process by putting code here.

list($err, $stdout, $stderr) = $gzip_future->resolve();

When you call resolve(), the future blocks until the result is ready. You can test if a future's result is ready by calling isReady():

$is_ready = $gzip_future->isReady();

Being "ready" indicates that the future's computation has completed and it will not need to block when you call resolve().

Note that when you instantiate a future, it does not immediately initiate computation. You must call start(), isReady() or resolve() to activate it. If you simply call resolve() it will start, block until it is complete, and then return the result, acting in a completely synchronous way.

See Command Execution for more detailed documentation on how to execute system commands with libphutil.

Managing Multiple Futures

Commonly, you may have many similar tasks you wish to parallelize: instead of compressing one file, you want to compress several files. You can use the FutureIterator class to manage multiple futures.

$futures = array();
foreach ($files as $file) {
  $futures[$file] = new ExecFuture("gzip %s", $file);
}
foreach (new FutureIterator($futures) as $file => $future) {
  list($err, $stdout, $stderr) = $future->resolve();
  if (!$err) {
    echo "Compressed {$file}...\n";
  } else {
    echo "Failed to compress {$file}!\n";
  }
}

FutureIterator takes a list of futures and runs them in parallel, returning them in the order they resolve, NOT the original list order. This allows your program to begin any follow-up computation as quickly as possible: if the slowest future in the list happens to be the first one, you can finish processing all the other futures while waiting for it.

You can also limit how many futures you want to run at once. For instance, to process no more than 4 files simultaneously:

foreach (id(new FutureIterator($futures))->limit(4) as $file => $future) {
  // ...
}

Consult the FutureIterator documentation for detailed information on class capabilities.