diff --git a/src/docs/article/using_futures.diviner b/src/docs/article/using_futures.diviner index a554fd6..f3bb179 100644 --- a/src/docs/article/using_futures.diviner +++ b/src/docs/article/using_futures.diviner @@ -1,91 +1,90 @@ @title Using Futures @group 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, @{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, via the convenience -function @{function:Futures}. +@{class:FutureIterator} class to manage multiple futures. $futures = array(); foreach ($files as $file) { $futures[$file] = new ExecFuture("gzip %s", $file); } - foreach (Futures($futures) as $file => $future) { + 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"; } } -@{function:Futures} takes a list of futures and runs them in parallel, +@{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 followup 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 (Futures($futures)->limit(4) as $file => $future) { + foreach (id(new FutureIterator($futures))->limit(4) as $file => $future) { // ... } Consult the @{class:FutureIterator} documentation for detailed information on class capabilities.