Changeset View
Changeset View
Standalone View
Standalone View
src/docs/article/using_futures.diviner
- This file was added.
| @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. | |||||