@title Using Futures
@group future

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,
@{class:ExecFuture} executes system commands while @{class: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 @{article: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
@{class: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";
    }
  }

@{class: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 @{class:FutureIterator} documentation for detailed information on
class capabilities.
